diff options
178 files changed, 1043 insertions, 569 deletions
diff --git a/.github/issue_template.md b/.github/issue_template.md index 2d071d4a71..2ff6a271db 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,7 +1,7 @@ ### Steps to reproduce (Guidelines for creating a bug report are [available -here](http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#creating-a-bug-report)) +here](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#creating-a-bug-report)) ### Expected behavior Tell us what should happen diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 214d63740c..a36687ec99 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -16,6 +16,6 @@ CHANGELOG files by reviewers, please add the CHANGELOG entry at the top of the f Finally, if your pull request affects documentation or any non-code changes, guidelines for those changes are [available -here](http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation) +here](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation) Thanks for contributing to Rails! diff --git a/.rubocop.yml b/.rubocop.yml index eb410376fe..afd5d6228d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,11 +9,17 @@ AllCops: - '**/templates/**/*' - '**/vendor/**/*' - 'actionpack/lib/action_dispatch/journey/parser.rb' + - 'railties/test/fixtures/tmp/**/*' # Prefer assert_not_x over refute_x CustomCops/RefuteNot: Include: - - '**/*_test.rb' + - '**/test/**/*' + +# Prefer assert_not over assert ! +CustomCops/AssertNot: + Include: + - '**/test/**/*' # Prefer &&/|| over and/or. Style/AndOr: diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 078d5f1219..ecd56b87d6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -8,5 +8,5 @@ http://rubyonrails.org/conduct/ For a history of updates, see the page history here: -https://github.com/rails/rails.github.com/commits/master/conduct/index.html +https://github.com/rails/homepage/commits/master/conduct.html @@ -1,5 +1,7 @@ # Welcome to Rails +## What's Rails + Rails is a web-application framework that includes everything needed to create database-backed web applications according to the [Model-View-Controller (MVC)](http://en.wikipedia.org/wiki/Model-view-controller) @@ -8,41 +10,46 @@ pattern. Understanding the MVC pattern is key to understanding Rails. MVC divides your application into three layers: Model, View, and Controller, each with a specific responsibility. -The _Model layer_ represents the domain model (such as Account, Product, +## Model layer + +The _**Model layer**_ represents the domain model (such as Account, Product, Person, Post, etc.) and encapsulates the business logic specific to your application. In Rails, database-backed model classes are derived from -`ActiveRecord::Base`. Active Record allows you to present the data from +`ActiveRecord::Base`. [Active Record](activerecord/README.rdoc) allows you to present the data from database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in its [README](activerecord/README.rdoc). +methods. Although most Rails models are backed by a database, models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by -the Active Model module. You can read more about Active Model in its [README](activemodel/README.rdoc). +the [Active Model](activemodel/README.rdoc) module. + +## Controller layer -The _Controller layer_ is responsible for handling incoming HTTP requests and +The _**Controller layer**_ is responsible for handling incoming HTTP requests and providing a suitable response. Usually this means returning HTML, but Rails controllers can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers load and manipulate models, and render view templates in order to generate the appropriate HTTP response. In Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and controller classes are derived from `ActionController::Base`. Action Dispatch and Action Controller -are bundled together in Action Pack. You can read more about Action Pack in its -[README](actionpack/README.rdoc). +are bundled together in [Action Pack](actionpack/README.rdoc). -The _View layer_ is composed of "templates" that are responsible for providing +## View layer + +The _**View layer**_ is composed of "templates" that are responsible for providing appropriate representations of your application's resources. Templates can come in a variety of formats, but most view templates are HTML with embedded Ruby code (ERB files). Views are typically rendered to generate a controller response, -or to generate the body of an email. In Rails, View generation is handled by Action View. -You can read more about Action View in its [README](actionview/README.rdoc). +or to generate the body of an email. In Rails, View generation is handled by [Action View](actionview/README.rdoc). + +## Frameworks and libraries -Active Record, Active Model, Action Pack, and Action View can each be used independently outside Rails. -In addition to that, Rails also comes with Action Mailer ([README](actionmailer/README.rdoc)), a library -to generate and send emails; Active Job ([README](activejob/README.md)), a +[Active Record](activerecord/README.rdoc), [Active Model](activemodel/README.rdoc), [Action Pack](actionpack/README.rdoc), and [Action View](actionview/README.rdoc) can each be used independently outside Rails. +In addition to that, Rails also comes with [Action Mailer](actionmailer/README.rdoc), a library +to generate and send emails; [Active Job](activejob/README.md), a framework for declaring jobs and making them run on a variety of queueing -backends; Action Cable ([README](actioncable/README.md)), a framework to -integrate WebSockets with a Rails application; -Active Storage ([README](activestorage/README.md)), a library to attach cloud +backends; [Action Cable](actioncable/README.md), a framework to +integrate WebSockets with a Rails application; [Active Storage](activestorage/README.md), a library to attach cloud and local files to Rails applications; -and Active Support ([README](activesupport/README.rdoc)), a collection +and [Active Support](activesupport/README.rdoc), a collection of utility classes and standard library extensions that are useful for Rails, and may also be used independently outside Rails. diff --git a/actioncable/test/channel/stream_test.rb b/actioncable/test/channel/stream_test.rb index e9e8849637..df9d44d8dd 100644 --- a/actioncable/test/channel/stream_test.rb +++ b/actioncable/test/channel/stream_test.rb @@ -200,7 +200,7 @@ module ActionCable::StreamTests end def receive(connection, command:, identifiers:, channel: "ActionCable::StreamTests::ChatChannel") - identifier = JSON.generate(channel: channel, **identifiers) + identifier = JSON.generate(identifiers.merge(channel: channel)) connection.dispatch_websocket_message JSON.generate(command: command, identifier: identifier) wait_for_async end diff --git a/actioncable/test/connection/base_test.rb b/actioncable/test/connection/base_test.rb index c69d4d0b36..ac10b3d4d2 100644 --- a/actioncable/test/connection/base_test.rb +++ b/actioncable/test/connection/base_test.rb @@ -83,7 +83,7 @@ class ActionCable::Connection::BaseTest < ActionCable::TestCase connection.subscriptions.expects(:unsubscribe_from_all) connection.send :handle_close - assert ! connection.connected + assert_not connection.connected assert_equal [], @server.connections end end diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb index 574840c30e..22f310f39f 100644 --- a/actionmailer/test/caching_test.rb +++ b/actionmailer/test/caching_test.rb @@ -40,14 +40,14 @@ class FragmentCachingTest < BaseCachingTest def test_fragment_exist_with_caching_enabled @store.write("views/name", "value") assert @mailer.fragment_exist?("name") - assert !@mailer.fragment_exist?("other_name") + assert_not @mailer.fragment_exist?("other_name") end def test_fragment_exist_with_caching_disabled @mailer.perform_caching = false @store.write("views/name", "value") - assert !@mailer.fragment_exist?("name") - assert !@mailer.fragment_exist?("other_name") + assert_not @mailer.fragment_exist?("name") + assert_not @mailer.fragment_exist?("other_name") end def test_write_fragment_with_caching_enabled @@ -90,7 +90,7 @@ class FragmentCachingTest < BaseCachingTest buffer = "generated till now -> ".html_safe buffer << view_context.send(:fragment_for, "expensive") { fragment_computed = true } - assert !fragment_computed + assert_not fragment_computed assert_equal "generated till now -> fragment content", buffer end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 152ec3700b..79209e908f 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,21 @@ +* Introduce ActionDispatch::DebugExceptions.register_interceptor + + Exception aware plugin authors can use the newly introduced + `.register_interceptor` method to get the processed exception, instead of + monkey patching DebugExceptions. + + ActionDispatch::DebugExceptions.register_interceptor do |request, exception| + HypoteticalPlugin.capture_exception(request, exception) + end + + *Genadi Samokovarov* + +* Output only one Content-Security-Policy nonce header value per request. + + Fixes #32597. + + *Andrey Novikov*, *Andrew White* + * Move default headers configuration into their own module that can be included in controllers. *Kevin Deisz* diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 4dd7c59ce8..e99eb1723a 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -28,7 +28,7 @@ namespace :test do end task :lines do - load File.expand_path("..", __dir__) + "/tools/line_statistics" + load File.expand_path("../tools/line_statistics", __dir__) files = FileList["lib/**/*.rb"] CodeTools::LineStatistics.new(files).print_loc end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 4789252550..5a06bf86e3 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -374,7 +374,7 @@ module ActionController # Person.new(params) # => #<Person id: nil, name: "Francesco"> def permit! each_pair do |key, value| - Array.wrap(value).each do |v| + Array.wrap(value).flatten.each do |v| v.permit! if v.respond_to? :permit! end end diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb index c1f80a1ffc..17e72b46ff 100644 --- a/actionpack/lib/action_dispatch/http/content_security_policy.rb +++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb @@ -21,13 +21,8 @@ module ActionDispatch #:nodoc: return response if policy_present?(headers) if policy = request.content_security_policy - if policy.directives["script-src"] - if nonce = request.content_security_policy_nonce - policy.directives["script-src"] << "'nonce-#{nonce}'" - end - end - - headers[header_name(request)] = policy.build(request.controller_instance) + nonce = request.content_security_policy_nonce + headers[header_name(request)] = policy.build(request.controller_instance, nonce) end response @@ -136,7 +131,9 @@ module ActionDispatch #:nodoc: worker_src: "worker-src" }.freeze - private_constant :MAPPINGS, :DIRECTIVES + NONCE_DIRECTIVES = %w[script-src].freeze + + private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES attr_reader :directives @@ -205,8 +202,8 @@ module ActionDispatch #:nodoc: end end - def build(context = nil) - build_directives(context).compact.join("; ") + def build(context = nil, nonce = nil) + build_directives(context, nonce).compact.join("; ") end private @@ -229,10 +226,14 @@ module ActionDispatch #:nodoc: end end - def build_directives(context) + def build_directives(context, nonce) @directives.map do |directive, sources| if sources.is_a?(Array) - "#{directive} #{build_directive(sources, context).join(' ')}" + if nonce && nonce_directive?(directive) + "#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'" + else + "#{directive} #{build_directive(sources, context).join(' ')}" + end elsif sources directive else @@ -261,5 +262,9 @@ module ActionDispatch #:nodoc: raise RuntimeError, "Unexpected content security policy source: #{source.inspect}" end end + + def nonce_directive?(directive) + NONCE_DIRECTIVES.include?(directive) + end end end diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index ec86b8bc47..ec012ad02d 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -9,8 +9,8 @@ module ActionDispatch # sub-hashes of the params hash to filter. Filtering only certain sub-keys # from a hash is possible by using the dot notation: 'credit_card.number'. # If a block is given, each key and value of the params hash and all - # sub-hashes is passed to it, where the value or the key can be replaced using - # String#replace or similar method. + # sub-hashes are passed to it, where the value or the key can be replaced using + # String#replace or similar methods. # # env["action_dispatch.parameter_filter"] = [:password] # => replaces the value to all keys matching /password/i with "[FILTERED]" diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index 511306eb0e..33edad8bd9 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -50,10 +50,18 @@ module ActionDispatch end end - def initialize(app, routes_app = nil, response_format = :default) + cattr_reader :interceptors, instance_accessor: false, default: [] + + def self.register_interceptor(object = nil, &block) + interceptor = object || block + interceptors << interceptor + end + + def initialize(app, routes_app = nil, response_format = :default, interceptors = self.class.interceptors) @app = app @routes_app = routes_app @response_format = response_format + @interceptors = interceptors end def call(env) @@ -67,12 +75,26 @@ module ActionDispatch response rescue Exception => exception + invoke_interceptors(request, exception) raise exception unless request.show_exceptions? render_exception(request, exception) end private + def invoke_interceptors(request, exception) + backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner") + wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) + + @interceptors.each do |interceptor| + begin + interceptor.call(request, exception) + rescue Exception + log_error(request, wrapper) + end + end + end + def render_exception(request, exception) backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner") wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb index fdc09bd951..4512ea27b3 100644 --- a/actionpack/test/abstract/callbacks_test.rb +++ b/actionpack/test/abstract/callbacks_test.rb @@ -154,7 +154,7 @@ module AbstractController test "when :except is specified, an after action is not triggered on that action" do @controller.process(:index) - assert !@controller.instance_variable_defined?("@authenticated") + assert_not @controller.instance_variable_defined?("@authenticated") end end @@ -198,7 +198,7 @@ module AbstractController test "when :except is specified with an array, an after action is not triggered on that action" do @controller.process(:index) - assert !@controller.instance_variable_defined?("@authenticated") + assert_not @controller.instance_variable_defined?("@authenticated") end end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 504c77b8ef..763df3a776 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -290,13 +290,13 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_template_objects_exist process :assign_this - assert !@controller.instance_variable_defined?(:"@hi") + assert_not @controller.instance_variable_defined?(:"@hi") assert @controller.instance_variable_get(:"@howdy") end def test_template_objects_missing process :nothing - assert !@controller.instance_variable_defined?(:@howdy) + assert_not @controller.instance_variable_defined?(:@howdy) end def test_empty_flash @@ -366,7 +366,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase process :redirect_external assert_predicate @response, :redirect? assert_match(/rubyonrails/, @response.redirect_url) - assert !/perloffrails/.match(@response.redirect_url) + assert_no_match(/perloffrails/, @response.redirect_url) end def test_redirection diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index a8f726f181..6fe036dd15 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -94,14 +94,14 @@ class FragmentCachingTest < ActionController::TestCase def test_fragment_exist_with_caching_enabled @store.write("views/name", "value") assert @controller.fragment_exist?("name") - assert !@controller.fragment_exist?("other_name") + assert_not @controller.fragment_exist?("other_name") end def test_fragment_exist_with_caching_disabled @controller.perform_caching = false @store.write("views/name", "value") - assert !@controller.fragment_exist?("name") - assert !@controller.fragment_exist?("other_name") + assert_not @controller.fragment_exist?("name") + assert_not @controller.fragment_exist?("other_name") end def test_write_fragment_with_caching_enabled @@ -144,7 +144,7 @@ class FragmentCachingTest < ActionController::TestCase buffer = "generated till now -> ".html_safe buffer << view_context.send(:fragment_for, "expensive") { fragment_computed = true } - assert !fragment_computed + assert_not fragment_computed assert_equal "generated till now -> fragment content", buffer end diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index 2b16a555bb..425a6e25cc 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -787,7 +787,7 @@ class FilterTest < ActionController::TestCase assert_equal %w( ensure_login find_user ), @controller.instance_variable_get(:@ran_filter) test_process(ConditionalSkippingController, "login") - assert !@controller.instance_variable_defined?("@ran_after_action") + assert_not @controller.instance_variable_defined?("@ran_after_action") test_process(ConditionalSkippingController, "change_password") assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_action") end diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb index 6c3ac26de1..e3ec5bb7fc 100644 --- a/actionpack/test/controller/flash_hash_test.rb +++ b/actionpack/test/controller/flash_hash_test.rb @@ -44,7 +44,7 @@ module ActionDispatch @hash["foo"] = "bar" @hash.delete "foo" - assert !@hash.key?("foo") + assert_not @hash.key?("foo") assert_nil @hash["foo"] end @@ -53,7 +53,7 @@ module ActionDispatch assert_equal({ "foo" => "bar" }, @hash.to_hash) @hash.to_hash["zomg"] = "aaron" - assert !@hash.key?("zomg") + assert_not @hash.key?("zomg") assert_equal({ "foo" => "bar" }, @hash.to_hash) end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 560157dc61..3f211cd60d 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -202,7 +202,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase test "validate_digest_response should fail with nil returning password_procedure" do @request.env["HTTP_AUTHORIZATION"] = encode_credentials(username: nil, password: nil) - assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret") { nil } + assert_not ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret") { nil } end test "authentication request with request-uri ending in '/'" do diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 8a49d7822e..9cdf04b886 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -135,7 +135,7 @@ class IntegrationTestTest < ActiveSupport::TestCase session1 = @test.open_session { |sess| } session2 = @test.open_session # implicit session - assert !session1.equal?(session2) + assert_not session1.equal?(session2) end # RSpec mixes Matchers (which has a #method_missing) into diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index e60003dc64..34b9ac0ab8 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -136,7 +136,7 @@ class ParametersPermitTest < ActiveSupport::TestCase test "key: it is not assigned if not present in params" do params = ActionController::Parameters.new(name: "Joe") permitted = params.permit(:id) - assert !permitted.has_key?(:id) + assert_not permitted.has_key?(:id) end test "key to empty array: empty arrays pass" do @@ -353,12 +353,15 @@ class ParametersPermitTest < ActiveSupport::TestCase assert_equal "Jonas", @params[:person][:family][:brother] end - test "permit is recursive" do + test "permit! is recursive" do + @params[:nested_array] = [[{ x: 2, y: 3 }, { x: 21, y: 42 }]] @params.permit! assert_predicate @params, :permitted? assert_predicate @params[:person], :permitted? assert_predicate @params[:person][:name], :permitted? assert_predicate @params[:person][:addresses][0], :permitted? + assert_predicate @params[:nested_array][0][0], :permitted? + assert_predicate @params[:nested_array][0][1], :permitted? end test "permitted takes a default value when Parameters.permit_all_parameters is set" do diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb index 95fce39dad..c4c7f53903 100644 --- a/actionpack/test/dispatch/content_security_policy_test.rb +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -200,7 +200,7 @@ class ContentSecurityPolicyTest < ActiveSupport::TestCase end def test_dynamic_directives - request = Struct.new(:host).new("www.example.com") + request = ActionDispatch::Request.new("HTTP_HOST" => "www.example.com") controller = Struct.new(:request).new(request) @policy.script_src -> { request.host } @@ -209,7 +209,9 @@ class ContentSecurityPolicyTest < ActiveSupport::TestCase def test_mixed_static_and_dynamic_directives @policy.script_src :self, -> { "foo.com" }, "bar.com" - assert_equal "script-src 'self' foo.com bar.com", @policy.build(Object.new) + request = ActionDispatch::Request.new({}) + controller = Struct.new(:request).new(request) + assert_equal "script-src 'self' foo.com bar.com", @policy.build(controller) end def test_invalid_directive_source @@ -241,6 +243,73 @@ class ContentSecurityPolicyTest < ActiveSupport::TestCase end end +class DefaultContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + def index + head :ok + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "default_content_security_policy_integration_test" do + get "/", to: "policy#index" + end + end + + POLICY = ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src :self + p.script_src :https + end + + class PolicyConfigMiddleware + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.content_security_policy"] = POLICY + env["action_dispatch.content_security_policy_nonce_generator"] = proc { "iyhD0Yc0W+c=" } + env["action_dispatch.content_security_policy_report_only"] = false + env["action_dispatch.show_exceptions"] = false + + @app.call(env) + end + end + + APP = build_app(ROUTES) do |middleware| + middleware.use PolicyConfigMiddleware + middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + end + + def app + APP + end + + def test_adds_nonce_to_script_src_content_security_policy_only_once + get "/" + get "/" + assert_policy "default-src 'self'; script-src https: 'nonce-iyhD0Yc0W+c='" + end + + private + + def assert_policy(expected, report_only: false) + assert_response :success + + if report_only + expected_header = "Content-Security-Policy-Report-Only" + unexpected_header = "Content-Security-Policy" + else + expected_header = "Content-Security-Policy" + unexpected_header = "Content-Security-Policy-Report-Only" + end + + assert_nil response.headers[unexpected_header] + assert_equal expected, response.headers[expected_header] + end +end + class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest class PolicyController < ActionController::Base content_security_policy only: :inline do |p| diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 94cff10fe4..aba778fad6 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -65,8 +65,8 @@ class CookieJarTest < ActiveSupport::TestCase end def test_key_methods - assert !request.cookie_jar.key?(:foo) - assert !request.cookie_jar.has_key?("foo") + assert_not request.cookie_jar.key?(:foo) + assert_not request.cookie_jar.has_key?("foo") request.cookie_jar[:foo] = :bar assert request.cookie_jar.key?(:foo) diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 60acba0616..045567ff83 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -3,6 +3,8 @@ require "abstract_unit" class DebugExceptionsTest < ActionDispatch::IntegrationTest + InterceptedErrorInstance = StandardError.new + class Boomer attr_accessor :closed @@ -36,6 +38,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest raise RuntimeError when %r{/method_not_allowed} raise ActionController::MethodNotAllowed + when %r{/intercepted_error} + raise InterceptedErrorInstance when %r{/unknown_http_method} raise ActionController::UnknownHttpMethod when %r{/not_implemented} @@ -76,9 +80,13 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end end + Interceptor = proc { |request, exception| request.set_header("int", exception) } + BadInterceptor = proc { |request, exception| raise "bad" } RoutesApp = Struct.new(:routes).new(SharedTestRoutes) ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp) DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp) + InterceptedApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :default, [Interceptor]) + BadInterceptedApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :default, [BadInterceptor]) test "skip diagnosis if not showing detailed exceptions" do @app = ProductionApp @@ -499,4 +507,20 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end end end + + test "invoke interceptors before rendering" do + @app = InterceptedApp + get "/intercepted_error", headers: { "action_dispatch.show_exceptions" => true } + + assert_equal InterceptedErrorInstance, request.get_header("int") + end + + test "bad interceptors doesn't debug exceptions" do + @app = BadInterceptedApp + + get "/puke", headers: { "action_dispatch.show_exceptions" => true } + + assert_response 500 + assert_match(/puke/, body) + end end diff --git a/actionpack/test/dispatch/executor_test.rb b/actionpack/test/dispatch/executor_test.rb index 8eb6450385..5b8be39b6d 100644 --- a/actionpack/test/dispatch/executor_test.rb +++ b/actionpack/test/dispatch/executor_test.rb @@ -81,7 +81,7 @@ class ExecutorTest < ActiveSupport::TestCase running = false body.close - assert !running + assert_not running end def test_complete_callbacks_are_called_on_close @@ -89,7 +89,7 @@ class ExecutorTest < ActiveSupport::TestCase executor.to_complete { completed = true } body = call_and_return_body - assert !completed + assert_not completed body.close assert completed @@ -116,7 +116,7 @@ class ExecutorTest < ActiveSupport::TestCase call_and_return_body.close assert result - assert !defined?(@in_shared_context) # it's not in the test itself + assert_not defined?(@in_shared_context) # it's not in the test itself end private diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 6167ea46df..fa264417e1 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -180,8 +180,8 @@ class MimeTypeTest < ActiveSupport::TestCase assert Mime[:js] =~ "text/javascript" assert Mime[:js] =~ "application/javascript" assert Mime[:js] !~ "text/html" - assert !(Mime[:js] !~ "text/javascript") - assert !(Mime[:js] !~ "application/javascript") + assert_not (Mime[:js] !~ "text/javascript") + assert_not (Mime[:js] !~ "application/javascript") assert Mime[:html] =~ "application/xhtml+xml" end end diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb index e529229fae..edc4cd62a3 100644 --- a/actionpack/test/dispatch/reloader_test.rb +++ b/actionpack/test/dispatch/reloader_test.rb @@ -115,7 +115,7 @@ class ReloaderTest < ActiveSupport::TestCase reloader.to_complete { completed = true } body = call_and_return_body - assert !completed + assert_not completed body.close assert completed @@ -129,7 +129,7 @@ class ReloaderTest < ActiveSupport::TestCase prepared = false body.close - assert !prepared + assert_not prepared end def test_complete_callbacks_are_called_on_exceptions diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 4c8d528507..6d87314e97 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -191,7 +191,7 @@ class ResponseTest < ActiveSupport::TestCase test "does not include Status header" do @response.status = "200 OK" _, headers, _ = @response.to_a - assert !headers.has_key?("Status") + assert_not headers.has_key?("Status") end test "response code" do diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index fe314e26b1..dd6adcbfd1 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -3166,7 +3166,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end - assert !respond_to?(:routes_no_collision_path) + assert_not respond_to?(:routes_no_collision_path) end def test_controller_name_with_leading_slash_raise_error diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 6bb1efb0ac..2c1ca12043 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,16 @@ +* Fix JavaScript views rendering does not work with Firefox when using + Content Security Policy. + + Fixes #32577. + + *Yuji Yaginuma* + +* Add the `nonce: true` option for `javascript_include_tag` helper to + support automatic nonce generation for Content Security Policy. + Works the same way as `javascript_tag nonce: true` does. + + *Yaroslav Markin* + * Remove `ActionView::Helpers::RecordTagHelper`. *Yoshiyuki Hirano* diff --git a/actionview/Rakefile b/actionview/Rakefile index 9542b626d1..bdfd96c141 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -131,7 +131,7 @@ namespace :assets do end task :lines do - load File.join(File.expand_path("..", __dir__), "/tools/line_statistics") + load File.expand_path("../tools/line_statistics", __dir__) files = FileList["lib/**/*.rb"] CodeTools::LineStatistics.new(files).print_loc end diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee index cf31c796df..019bda635a 100644 --- a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee +++ b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee @@ -66,7 +66,7 @@ processResponse = (response, type) -> try response = JSON.parse(response) else if type.match(/\b(?:java|ecma)script\b/) script = document.createElement('script') - script.nonce = cspNonce() + script.setAttribute('nonce', cspNonce()) script.text = response document.head.appendChild(script).parentNode.removeChild(script) else if type.match(/\bxml\b/) diff --git a/actionview/lib/action_view/context.rb b/actionview/lib/action_view/context.rb index 665a9e3171..3c605c3ee3 100644 --- a/actionview/lib/action_view/context.rb +++ b/actionview/lib/action_view/context.rb @@ -10,10 +10,11 @@ module ActionView # Action View contexts are supplied to Action Controller to render a template. # The default Action View context is ActionView::Base. # - # In order to work with ActionController, a Context must just include this module. - # The initialization of the variables used by the context (@output_buffer, @view_flow, - # and @virtual_path) is responsibility of the object that includes this module - # (although you can call _prepare_context defined below). + # In order to work with Action Controller, a Context must just include this + # module. The initialization of the variables used by the context + # (@output_buffer, @view_flow, and @virtual_path) is responsibility of the + # object that includes this module (although you can call _prepare_context + # defined below). module Context include CompiledTemplates attr_accessor :output_buffer, :view_flow diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index 45cf48b3e0..3832293251 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -71,11 +71,16 @@ module ActionView private def find_template(finder, *args) + name = args.first + prefixes = args[1] || [] + partial = args[2] || false + keys = args[3] || [] + options = args[4] || {} finder.disable_cache do if format = finder.rendered_format - finder.find_all(*args, formats: [format]).first || finder.find_all(*args).first + finder.find_all(name, prefixes, partial, keys, options.merge(formats: [format])).first || finder.find_all(name, prefixes, partial, keys, options).first else - finder.find_all(*args).first + finder.find_all(name, prefixes, partial, keys, options).first end end end diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index 06fa1875fc..257080d902 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -55,6 +55,8 @@ module ActionView # that path. # * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline # when it is set to true. + # * <tt>:nonce<tt> - When set to true, adds an automatic nonce value if + # you have Content Security Policy enabled. # # ==== Examples # @@ -79,6 +81,9 @@ module ActionView # # javascript_include_tag "http://www.example.com/xmlhr.js" # # => <script src="http://www.example.com/xmlhr.js"></script> + # + # javascript_include_tag "http://www.example.com/xmlhr.js", nonce: true + # # => <script src="http://www.example.com/xmlhr.js" nonce="..."></script> def javascript_include_tag(*sources) options = sources.extract_options!.stringify_keys path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys @@ -90,6 +95,9 @@ module ActionView tag_options = { "src" => href }.merge!(options) + if tag_options["nonce"] == true + tag_options["nonce"] = content_security_policy_nonce + end content_tag("script".freeze, "", tag_options) }.join("\n").html_safe diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 54f82e058e..ba09738beb 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -389,8 +389,8 @@ module ActionView # * Any other key creates standard HTML options for the tag. # # ==== Examples - # radio_button_tag 'gender', 'male' - # # => <input id="gender_male" name="gender" type="radio" value="male" /> + # radio_button_tag 'favorite_color', 'maroon' + # # => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" /> # # radio_button_tag 'receive_updates', 'no', true # # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" /> diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index 80cb73d683..db44fdbfee 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -60,7 +60,11 @@ module ActionView def translate(key, options = {}) options = options.dup has_default = options.has_key?(:default) - remaining_defaults = Array(options.delete(:default)).compact + if has_default + remaining_defaults = Array(options.delete(:default)).compact + else + remaining_defaults = [] + end if has_default && !remaining_defaults.first.kind_of?(Symbol) options[:default] = remaining_defaults diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index 6d98eacfb8..e68f03d1f4 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -29,6 +29,10 @@ class AssetTagHelperTest < ActionView::TestCase "http://www.example.com" end + def content_security_policy_nonce + "iyhD0Yc0W+c=" + end + AssetPathToTag = { %(asset_path("")) => %(), %(asset_path(" ")) => %(), @@ -421,6 +425,10 @@ class AssetTagHelperTest < ActionView::TestCase assert_dom_equal %(<script src="//assets.example.com/javascripts/prototype.js"></script>), javascript_include_tag("prototype") end + def test_javascript_include_tag_nonce + assert_dom_equal %(<script src="/javascripts/bank.js" nonce="iyhD0Yc0W+c="></script>), javascript_include_tag("bank", nonce: true) + end + def test_stylesheet_path StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb index 31c280a91c..131e49327e 100644 --- a/actionview/test/template/capture_helper_test.rb +++ b/actionview/test/template/capture_helper_test.rb @@ -49,21 +49,21 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_with_multiple_calls - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title, "foo" content_for :title, "bar" assert_equal "foobar", content_for(:title) end def test_content_for_with_multiple_calls_and_flush - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title, "foo" content_for :title, "bar", flush: true assert_equal "bar", content_for(:title) end def test_content_for_with_block - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title do output_buffer << "foo" output_buffer << "bar" @@ -73,7 +73,7 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_with_block_and_multiple_calls_with_flush - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title do "foo" end @@ -84,7 +84,7 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_with_block_and_multiple_calls_with_flush_nil_content - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title do "foo" end @@ -95,7 +95,7 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_with_block_and_multiple_calls_without_flush - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title do "foo" end @@ -106,7 +106,7 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_with_whitespace_block - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title, "foo" content_for :title do output_buffer << " \n " @@ -117,7 +117,7 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_with_whitespace_block_and_flush - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title, "foo" content_for :title, flush: true do output_buffer << " \n " @@ -128,7 +128,7 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_returns_nil_when_writing - assert ! content_for?(:title) + assert_not content_for?(:title) assert_nil content_for(:title, "foo") assert_nil content_for(:title) { output_buffer << "bar"; nil } assert_nil content_for(:title) { output_buffer << " \n "; nil } @@ -144,14 +144,14 @@ class CaptureHelperTest < ActionView::TestCase end def test_content_for_question_mark - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title, "title" assert content_for?(:title) - assert ! content_for?(:something_else) + assert_not content_for?(:something_else) end def test_content_for_should_be_html_safe_after_flush_empty - assert ! content_for?(:title) + assert_not content_for?(:title) content_for :title do content_tag(:p, "title") end @@ -164,7 +164,7 @@ class CaptureHelperTest < ActionView::TestCase end def test_provide - assert !content_for?(:title) + assert_not content_for?(:title) provide :title, "hi" assert content_for?(:title) assert_equal "hi", content_for(:title) diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb index beee76f711..38469cbe3d 100644 --- a/actionview/test/template/lookup_context_test.rb +++ b/actionview/test/template/lookup_context_test.rb @@ -195,7 +195,7 @@ class LookupContextTest < ActiveSupport::TestCase assert @lookup_context.cache template = @lookup_context.disable_cache do - assert !@lookup_context.cache + assert_not @lookup_context.cache @lookup_context.find("foo", %w(test), true) end assert @lookup_context.cache diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index 05e5f21ce4..d98fd4f9a2 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -192,7 +192,7 @@ module ActionView helper HelperThatInvokesProtectAgainstForgery test "protect_from_forgery? in any helpers returns false" do - assert !view.help_me + assert_not view.help_me end end diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index 8bccda481b..08cb5dfea7 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -508,16 +508,16 @@ class UrlHelperTest < ActiveSupport::TestCase def test_current_page_considering_params @request = request_for_url("/?order=desc&page=1") - assert !current_page?(url_hash, check_parameters: true) - assert !current_page?(url_hash.merge(check_parameters: true)) - assert !current_page?(ActionController::Parameters.new(url_hash.merge(check_parameters: true)).permit!) - assert !current_page?("http://www.example.com/", check_parameters: true) + assert_not current_page?(url_hash, check_parameters: true) + assert_not current_page?(url_hash.merge(check_parameters: true)) + assert_not current_page?(ActionController::Parameters.new(url_hash.merge(check_parameters: true)).permit!) + assert_not current_page?("http://www.example.com/", check_parameters: true) end def test_current_page_considering_params_when_options_does_not_respond_to_to_hash @request = request_for_url("/?order=desc&page=1") - assert !current_page?(:back, check_parameters: false) + assert_not current_page?(:back, check_parameters: false) end def test_current_page_with_params_that_match @@ -562,7 +562,7 @@ class UrlHelperTest < ActiveSupport::TestCase def test_current_page_with_not_get_verb @request = request_for_url("/events", method: :post) - assert !current_page?("/events") + assert_not current_page?("/events") end def test_link_unless_current diff --git a/actionview/test/ujs/public/test/call-remote-callbacks.js b/actionview/test/ujs/public/test/call-remote-callbacks.js index 48763f6301..9c0c8cfb4b 100644 --- a/actionview/test/ujs/public/test/call-remote-callbacks.js +++ b/actionview/test/ujs/public/test/call-remote-callbacks.js @@ -75,9 +75,9 @@ asyncTest('setting data("with-credentials",true) with "ajax:before" uses new set asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function() { submit(function(form) { - form.bindNative('ajax:beforeSend', function() { + form.bindNative('ajax:beforeSend', function(e) { ok(true, 'aborting request in ajax:beforeSend') - return false + e.preventDefault() }) form.unbind('ajax:send').bindNative('ajax:send', function() { ok(false, 'ajax:send should not run') @@ -148,8 +148,8 @@ function skipIt() { .bind('iframe:loading', function() { ok(false, 'form should not get submitted') }) - .bindNative('ajax:aborted:file', function() { - return false + .bindNative('ajax:aborted:file', function(e) { + e.preventDefault() }) .triggerNative('submit') @@ -162,9 +162,9 @@ function skipIt() { } asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', 1, function() { - $(document).delegate('form[data-remote]', 'ajax:beforeSend', function() { + $(document).delegate('form[data-remote]', 'ajax:beforeSend', function(e) { ok(true, 'ajax:beforeSend observed with event delegation') - return false + e.preventDefault() }) submit(function(form) { diff --git a/actionview/test/ujs/public/test/call-remote.js b/actionview/test/ujs/public/test/call-remote.js index e5277a2a03..778dc1b09a 100644 --- a/actionview/test/ujs/public/test/call-remote.js +++ b/actionview/test/ujs/public/test/call-remote.js @@ -210,7 +210,7 @@ asyncTest('allow empty form "action"', 1, function() { buildForm({ action: '' }) $('#qunit-fixture').find('form') - .bindNative('ajax:beforeSend', function(e, xhr, settings) { + .bindNative('ajax:beforeSend', function(evt, xhr, settings) { // Get current location (the same way jQuery does) try { currentLocation = location.href @@ -229,7 +229,7 @@ asyncTest('allow empty form "action"', 1, function() { // Prevent the request from actually getting sent to the current page and // causing an error. - return false + evt.preventDefault() }) .triggerNative('submit') @@ -257,7 +257,7 @@ asyncTest('intelligently guesses crossDomain behavior when target URL has a diff equal(settings.crossDomain, true, 'crossDomain should be set to true') // prevent request from actually getting sent off-domain - return false + evt.preventDefault() }) .triggerNative('submit') @@ -276,7 +276,7 @@ asyncTest('intelligently guesses crossDomain behavior when target URL consists o equal(settings.crossDomain, false, 'crossDomain should be set to false') // prevent request from actually getting sent off-domain - return false + evt.preventDefault() }) .triggerNative('submit') diff --git a/actionview/test/ujs/public/test/data-confirm.js b/actionview/test/ujs/public/test/data-confirm.js index 74f373148f..1bd57b69ad 100644 --- a/actionview/test/ujs/public/test/data-confirm.js +++ b/actionview/test/ujs/public/test/data-confirm.js @@ -173,9 +173,9 @@ asyncTest('binding to confirm event of a link and returning false', 1, function( } $('a[data-confirm]') - .bindNative('confirm', function() { + .bindNative('confirm', function(e) { App.assertCallbackInvoked('confirm') - return false + e.preventDefault() }) .bindNative('confirm:complete', function() { App.assertCallbackNotInvoked('confirm:complete') @@ -194,9 +194,9 @@ asyncTest('binding to confirm event of a button and returning false', 1, functio } $('button[data-confirm]') - .bindNative('confirm', function() { + .bindNative('confirm', function(e) { App.assertCallbackInvoked('confirm') - return false + e.preventDefault() }) .bindNative('confirm:complete', function() { App.assertCallbackNotInvoked('confirm:complete') @@ -216,9 +216,9 @@ asyncTest('binding to confirm:complete event of a link and returning false', 2, } $('a[data-confirm]') - .bindNative('confirm:complete', function() { + .bindNative('confirm:complete', function(e) { App.assertCallbackInvoked('confirm:complete') - return false + e.preventDefault() }) .bindNative('ajax:beforeSend', function() { App.assertCallbackNotInvoked('ajax:beforeSend') @@ -238,9 +238,9 @@ asyncTest('binding to confirm:complete event of a button and returning false', 2 } $('button[data-confirm]') - .bindNative('confirm:complete', function() { + .bindNative('confirm:complete', function(e) { App.assertCallbackInvoked('confirm:complete') - return false + e.preventDefault() }) .bindNative('ajax:beforeSend', function() { App.assertCallbackNotInvoked('ajax:beforeSend') diff --git a/actionview/test/ujs/public/test/data-disable-with.js b/actionview/test/ujs/public/test/data-disable-with.js index b29cbbc867..645ad494c3 100644 --- a/actionview/test/ujs/public/test/data-disable-with.js +++ b/actionview/test/ujs/public/test/data-disable-with.js @@ -132,7 +132,8 @@ test('form input[type=submit][data-disable-with] re-enables when `pageshow` even }) asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced in ajax callback', 2, function() { - var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html() + var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'), + origFormContents = form.html() form.bindNative('ajax:success', function() { form.html(origFormContents) @@ -146,7 +147,8 @@ asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced i }) asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled field in ajax callback', 2, function() { - var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'), + var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'), + input = form.find('input[type=submit]'), newDisabledInput = input.clone().attr('disabled', 'disabled') form.bindNative('ajax:success', function() { @@ -238,9 +240,9 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:before` event App.checkEnabledState(link, 'Click me') link - .bindNative('ajax:before', function() { + .bindNative('ajax:before', function(e) { App.checkDisabledState(link, 'clicking...') - return false + e.preventDefault() }) .triggerNative('click') @@ -256,9 +258,9 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:beforeSend` e App.checkEnabledState(link, 'Click me') link - .bindNative('ajax:beforeSend', function() { + .bindNative('ajax:beforeSend', function(e) { App.checkDisabledState(link, 'clicking...') - return false + e.preventDefault() }) .triggerNative('click') @@ -293,8 +295,9 @@ asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not d submit = $('<input type="submit" data-disable-with="submitting ..." name="submit2" value="Submit" />').appendTo(form) form - .bindNative('ajax:beforeSend', function() { - return false + .bindNative('ajax:beforeSend', function(e) { + e.preventDefault() + e.stopPropagation() }) .triggerNative('submit') @@ -343,9 +346,9 @@ asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:before` App.checkEnabledState(button, 'Click me') button - .bindNative('ajax:before', function() { + .bindNative('ajax:before', function(e) { App.checkDisabledState(button, 'clicking...') - return false + e.preventDefault() }) .triggerNative('click') @@ -361,9 +364,9 @@ asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:beforeSe App.checkEnabledState(button, 'Click me') button - .bindNative('ajax:beforeSend', function() { + .bindNative('ajax:beforeSend', function(e) { App.checkDisabledState(button, 'clicking...') - return false + e.preventDefault() }) .triggerNative('click') diff --git a/actionview/test/ujs/public/test/data-disable.js b/actionview/test/ujs/public/test/data-disable.js index ccc38cf9ae..e9919764b6 100644 --- a/actionview/test/ujs/public/test/data-disable.js +++ b/actionview/test/ujs/public/test/data-disable.js @@ -91,7 +91,7 @@ asyncTest('form input[type=submit][data-disable] disables', 6, function() { }) asyncTest('form[data-remote] input[type=submit][data-disable] is replaced in ajax callback', 2, function() { - var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html() + var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html() form.bindNative('ajax:success', function() { form.html(origFormContents) @@ -105,7 +105,7 @@ asyncTest('form[data-remote] input[type=submit][data-disable] is replaced in aja }) asyncTest('form[data-remote] input[data-disable] is replaced with disabled field in ajax callback', 2, function() { - var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'), + var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'), newDisabledInput = input.clone().attr('disabled', 'disabled') form.bindNative('ajax:success', function() { @@ -168,9 +168,9 @@ asyncTest('a[data-remote][data-disable] re-enables when `ajax:before` event is c App.checkEnabledState(link, 'Click me') link - .bindNative('ajax:before', function() { + .bindNative('ajax:before', function(e) { App.checkDisabledState(link, 'Click me') - return false + e.preventDefault() }) .triggerNative('click') @@ -186,9 +186,9 @@ asyncTest('a[data-remote][data-disable] re-enables when `ajax:beforeSend` event App.checkEnabledState(link, 'Click me') link - .bindNative('ajax:beforeSend', function() { + .bindNative('ajax:beforeSend', function(e) { App.checkDisabledState(link, 'Click me') - return false + e.preventDefault() }) .triggerNative('click') @@ -223,8 +223,9 @@ asyncTest('form[data-remote] input|button|textarea[data-disable] does not disabl submit = $('<input type="submit" data-disable="submitting ..." name="submit2" value="Submit" />').appendTo(form) form - .bindNative('ajax:beforeSend', function() { - return false + .bindNative('ajax:beforeSend', function(e) { + e.preventDefault() + e.stopPropagation() }) .triggerNative('submit') @@ -273,9 +274,9 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:before` event App.checkEnabledState(button, 'Click me') button - .bindNative('ajax:before', function() { + .bindNative('ajax:before', function(e) { App.checkDisabledState(button, 'Click me') - return false + e.preventDefault() }) .triggerNative('click') @@ -291,9 +292,9 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:beforeSend` e App.checkEnabledState(button, 'Click me') button - .bindNative('ajax:beforeSend', function() { + .bindNative('ajax:beforeSend', function(e) { App.checkDisabledState(button, 'Click me') - return false + e.preventDefault() }) .triggerNative('click') diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js index cbbd4e6c92..3503c2cff3 100644 --- a/actionview/test/ujs/public/test/data-remote.js +++ b/actionview/test/ujs/public/test/data-remote.js @@ -272,9 +272,10 @@ asyncTest('returning false in form\'s submit bindings in non-submit-bubbling bro form .append($('<input type="submit" />')) - .bindNative('submit', function() { + .bindNative('submit', function(e) { ok(true, 'binding handler is called') - return false + e.preventDefault() + e.stopPropagation() }) .bindNative('ajax:beforeSend', function() { ok(false, 'form should not be submitted') @@ -296,8 +297,8 @@ asyncTest('clicking on a link with falsy "data-remote" attribute does not fire a .bindNative('ajax:beforeSend', function() { ok(false, 'ajax should not be triggered') }) - .bindNative('click', function() { - return false + .bindNative('click', function(e) { + e.preventDefault() }) .triggerNative('click') @@ -314,8 +315,8 @@ asyncTest('ctrl-clicking on a link with falsy "data-remote" attribute does not f .bindNative('ajax:beforeSend', function() { ok(false, 'ajax should not be triggered') }) - .bindNative('click', function() { - return false + .bindNative('click', function(e) { + e.preventDefault() }) .triggerNative('click', { metaKey: true }) @@ -333,8 +334,8 @@ asyncTest('clicking on a button with falsy "data-remote" attribute', 0, function .bindNative('ajax:beforeSend', function() { ok(false, 'ajax should not be triggered') }) - .bindNative('click', function() { - return false + .bindNative('click', function(e) { + e.preventDefault() }) .triggerNative('click') @@ -347,8 +348,8 @@ asyncTest('submitting a form with falsy "data-remote" attribute', 0, function() .bindNative('ajax:beforeSend', function() { ok(false, 'ajax should not be triggered') }) - .bindNative('submit', function() { - return false + .bindNative('submit', function(e) { + e.preventDefault() }) .triggerNative('submit') @@ -429,7 +430,7 @@ asyncTest('changing a select option without "data-url" attribute still fires aja ajaxLocation = settings.url.replace(settings.data, '').replace(/&$/, '').replace(/\?$/, '') equal(ajaxLocation, currentLocation, 'URL should be current page by default') - return false + e.preventDefault() }) .val('optionValue2') .triggerNative('change') diff --git a/actionview/test/ujs/public/test/override.js b/actionview/test/ujs/public/test/override.js index 299c7018cc..d73276ee4f 100644 --- a/actionview/test/ujs/public/test/override.js +++ b/actionview/test/ujs/public/test/override.js @@ -25,7 +25,7 @@ asyncTest('the getter for an element\'s href is overridable', 1, function() { $('#qunit-fixture a') .bindNative('ajax:beforeSend', function(e, xhr, options) { equal('/data/href', options.url) - return false + e.preventDefault() }) .triggerNative('click') start() @@ -35,7 +35,7 @@ asyncTest('the getter for an element\'s href works normally if not overridden', $('#qunit-fixture a') .bindNative('ajax:beforeSend', function(e, xhr, options) { equal(location.protocol + '//' + location.host + '/real/href', options.url) - return false + e.preventDefault() }) .triggerNative('click') start() diff --git a/actionview/test/ujs/public/test/settings.js b/actionview/test/ujs/public/test/settings.js index 299c71bb00..b1ce3b8c64 100644 --- a/actionview/test/ujs/public/test/settings.js +++ b/actionview/test/ujs/public/test/settings.js @@ -103,14 +103,16 @@ $.fn.extend({ bindNative: function(event, handler) { if (!handler) return this - this.bind(event, function(e) { + var el = this[0] + el.addEventListener(event, function(e) { var args = [] - if (e.originalEvent.detail) { - args = e.originalEvent.detail.slice() + if (e.detail) { + args = e.detail.slice() } args.unshift(e) - return handler.apply(this, args) - }) + return handler.apply(el, args) + }, false) + return this } }) diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index 3104e7e329..9c12dc14c5 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -19,7 +19,7 @@ module ActiveModel # particular enumerable object. # # class Person < ActiveRecord::Base - # validates_inclusion_of :gender, in: %w( m f ) + # validates_inclusion_of :role, in: %w( admin contributor ) # validates_inclusion_of :age, in: 0..99 # validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list" # validates_inclusion_of :states, in: ->(person) { STATES[person.country] } diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index e28e7e9219..88cca318ef 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -63,7 +63,7 @@ module ActiveModel # and strings in shortcut form. # # validates :email, format: /@/ - # validates :gender, inclusion: %w(male female) + # validates :role, inclusion: %(admin contributor) # validates :password, length: 6..20 # # When using shortcut form, ranges and arrays are passed to your diff --git a/activemodel/test/cases/attributes_dirty_test.rb b/activemodel/test/cases/attributes_dirty_test.rb index c991176389..f9693a23cd 100644 --- a/activemodel/test/cases/attributes_dirty_test.rb +++ b/activemodel/test/cases/attributes_dirty_test.rb @@ -39,7 +39,7 @@ class AttributesDirtyTest < ActiveModel::TestCase end test "changes to attribute values" do - assert !@model.changes["name"] + assert_not @model.changes["name"] @model.name = "John" assert_equal [nil, "John"], @model.changes["name"] end diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb index f769eb0da1..b120e68027 100644 --- a/activemodel/test/cases/dirty_test.rb +++ b/activemodel/test/cases/dirty_test.rb @@ -78,7 +78,7 @@ class DirtyTest < ActiveModel::TestCase end test "changes to attribute values" do - assert !@model.changes["name"] + assert_not @model.changes["name"] @model.name = "John" assert_equal [nil, "John"], @model.changes["name"] end diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index cb6a8c43d5..6ff3be1308 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -207,26 +207,26 @@ class ErrorsTest < ActiveModel::TestCase test "added? returns false when no errors are present" do person = Person.new - assert !person.errors.added?(:name) + assert_not person.errors.added?(:name) end test "added? returns false when checking a nonexisting error and other errors are present for the given attribute" do person = Person.new person.errors.add(:name, "is invalid") - assert !person.errors.added?(:name, "cannot be blank") + assert_not person.errors.added?(:name, "cannot be blank") end test "added? returns false when checking for an error, but not providing message arguments" do person = Person.new person.errors.add(:name, "cannot be blank") - assert !person.errors.added?(:name) + assert_not person.errors.added?(:name) end test "added? returns false when checking for an error by symbol and a different error with same message is present" do I18n.backend.store_translations("en", errors: { attributes: { name: { wrong: "is wrong", used: "is wrong" } } }) person = Person.new person.errors.add(:name, :wrong) - assert !person.errors.added?(:name, :used) + assert_not person.errors.added?(:name, :used) end test "size calculates the number of error messages" do diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index d19e81a119..912cb6d5ab 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -187,7 +187,7 @@ class SecurePasswordTest < ActiveModel::TestCase test "authenticate" do @user.password = "secret" - assert !@user.authenticate("wrong") + assert_not @user.authenticate("wrong") assert @user.authenticate("secret") end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 08f3e15a4b..41553cfa83 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -385,7 +385,7 @@ module ActiveRecord end end - def empty_insert_statement_value + def empty_insert_statement_value(primary_key = nil) "DEFAULT VALUES" end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index d9ac8db6a8..0ce3796829 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -75,7 +75,6 @@ module ActiveRecord class Transaction #:nodoc: attr_reader :connection, :state, :records, :savepoint_name - attr_writer :joinable def initialize(connection, options, run_commit_callbacks: false) @connection = connection diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index fbedddd7f9..477b09944f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -223,7 +223,7 @@ module ActiveRecord end end - def empty_insert_statement_value + def empty_insert_statement_value(primary_key = nil) "VALUES ()" end diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 9234029c22..013c3765b2 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -125,7 +125,7 @@ module ActiveRecord ] end - RAILS_GEM_ROOT = File.expand_path("../../../..", __FILE__) + "/" + RAILS_GEM_ROOT = File.expand_path("../../..", __dir__) + "/" def ignored_callstack(path) path.start_with?(RAILS_GEM_ROOT) || diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 663b3c590a..025201c20b 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "benchmark" require "set" require "zlib" require "active_support/core_ext/module/attribute_accessors" diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 7721e6b691..c2393c1fc8 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -178,7 +178,7 @@ module ActiveRecord end if values.empty? - im = arel_table.compile_insert(connection.empty_insert_statement_value) + im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key)) im.into arel_table else im = arel_table.compile_insert(_substitute_values(values)) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3c66d2f8be..24b9bab0cd 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -557,7 +557,7 @@ module ActiveRecord ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins) end - def preload_associations(records) + def preload_associations(records) # :nodoc: preload = preload_values preload += includes_values unless eager_loading? preloader = nil diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4e60863e52..a180b0f0d3 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -894,8 +894,8 @@ module ActiveRecord self end - def skip_query_cache! # :nodoc: - self.skip_query_cache_value = true + def skip_query_cache!(value = true) # :nodoc: + self.skip_query_cache_value = value self end diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 4c41a68407..02c7e47583 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -84,7 +84,7 @@ module ActiveRecord indexes = @connection.indexes("accounts") assert_equal "accounts", indexes.first.table assert_equal idx_name, indexes.first.name - assert !indexes.first.unique + assert_not indexes.first.unique assert_equal ["firm_id"], indexes.first.columns ensure @connection.remove_index(:accounts, name: idx_name) rescue nil diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index 9ae2c42368..976c5dde58 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -148,8 +148,8 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase t.timestamps null: true end ActiveRecord::Base.connection.remove_timestamps :delete_me, null: true - assert !column_present?("delete_me", "updated_at", "datetime") - assert !column_present?("delete_me", "created_at", "datetime") + assert_not column_present?("delete_me", "updated_at", "datetime") + assert_not column_present?("delete_me", "created_at", "datetime") ensure ActiveRecord::Base.connection.drop_table :delete_me rescue nil end diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 140d7cbcae..f05dcac7dd 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -116,8 +116,8 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase end end - assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null + assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null end def test_timestamps_without_null_set_null_to_false_on_change_table @@ -129,8 +129,8 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase end end - assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null + assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null end def test_timestamps_without_null_set_null_to_false_on_add_timestamps @@ -139,7 +139,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase add_timestamps :has_timestamps, default: Time.now end - assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null - assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null + assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null + assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null end end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 5f771fe85f..5d9735d98a 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -569,7 +569,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer = Developer.create name: "Bryan", salary: 50_000 assert_not_predicate project.developers, :loaded? - assert ! project.developers.include?(developer) + assert_not project.developers.include?(developer) end def test_find_with_merged_options diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index a64432fae7..b00a3880f6 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -2009,7 +2009,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_calling_none_on_loaded_association_should_not_use_query firm = companies(:first_firm) firm.clients.load # force load - assert_no_queries { assert ! firm.clients.none? } + assert_no_queries { assert_not firm.clients.none? } end def test_calling_none_should_defer_to_collection_if_using_a_block @@ -2044,7 +2044,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_calling_one_on_loaded_association_should_not_use_query firm = companies(:first_firm) firm.clients.load # force load - assert_no_queries { assert ! firm.clients.one? } + assert_no_queries { assert_not firm.clients.one? } end def test_calling_one_should_defer_to_collection_if_using_a_block diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 3b3d4037b9..0facc286da 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -866,7 +866,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase author = authors(:mary) category = author.named_categories.create(name: "Primary") author.named_categories.delete(category) - assert !Categorization.exists?(author_id: author.id, named_category_name: category.name) + assert_not Categorization.exists?(author_id: author.id, named_category_name: category.name) assert_empty author.named_categories.reload end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index b90edf9b13..d7e898a1c0 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -452,7 +452,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_equal new_ship, pirate.ship assert_predicate new_ship, :new_record? assert_nil orig_ship.pirate_id - assert !orig_ship.changed? # check it was saved + assert_not orig_ship.changed? # check it was saved end def test_creation_failure_with_dependent_option diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 57ebc1e3e0..9d1c73c33b 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -732,7 +732,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase category = Category.create!(name: "Not Associated") assert_not_predicate david.categories, :loaded? - assert ! david.categories.include?(category) + assert_not david.categories.include?(category) end def test_has_many_through_goes_through_all_sti_classes diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index dc6638d45d..1a37ad963f 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -63,8 +63,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase t.author_name = "" assert t.attribute_present?("title") assert t.attribute_present?("written_on") - assert !t.attribute_present?("content") - assert !t.attribute_present?("author_name") + assert_not t.attribute_present?("content") + assert_not t.attribute_present?("author_name") end test "attribute_present with booleans" do @@ -77,7 +77,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert b2.attribute_present?(:value) b3 = Boolean.new - assert !b3.attribute_present?(:value) + assert_not b3.attribute_present?(:value) b4 = Boolean.new b4.value = false @@ -827,7 +827,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase self.table_name = "computers" end - assert !klass.instance_method_already_implemented?(:system) + assert_not klass.instance_method_already_implemented?(:system) computer = klass.new assert_nil computer.system end @@ -841,8 +841,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase self.table_name = "computers" end - assert !klass.instance_method_already_implemented?(:system) - assert !subklass.instance_method_already_implemented?(:system) + assert_not klass.instance_method_already_implemented?(:system) + assert_not subklass.instance_method_already_implemented?(:system) computer = subklass.new assert_nil computer.system end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index b8243d148a..7915599f72 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -116,7 +116,7 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas assert_not_predicate firm.account, :valid? assert_not_predicate firm, :valid? - assert !firm.save + assert_not firm.save assert_equal ["is invalid"], firm.errors["account"] end @@ -237,7 +237,7 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test log.developer = Developer.new assert_not_predicate log.developer, :valid? assert_not_predicate log, :valid? - assert !log.save + assert_not log.save assert_equal ["is invalid"], log.errors["developer"] end @@ -499,10 +499,10 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa def test_invalid_adding firm = Firm.find(1) - assert !(firm.clients_of_firm << c = Client.new) + assert_not (firm.clients_of_firm << c = Client.new) assert_not_predicate c, :persisted? assert_not_predicate firm, :valid? - assert !firm.save + assert_not firm.save assert_not_predicate c, :persisted? end @@ -512,7 +512,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa assert_not_predicate c, :persisted? assert_not_predicate c, :valid? assert_not_predicate new_firm, :valid? - assert !new_firm.save + assert_not new_firm.save assert_not_predicate c, :persisted? assert_not_predicate new_firm, :persisted? end @@ -550,7 +550,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa assert_not_predicate new_client, :persisted? assert_not_predicate new_client, :valid? assert_equal new_client, companies(:first_firm).clients_of_firm.last - assert !companies(:first_firm).save + assert_not companies(:first_firm).save assert_not_predicate new_client, :persisted? assert_equal 2, companies(:first_firm).clients_of_firm.reload.size end @@ -795,7 +795,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase @ship.pirate.catchphrase = "Changed Catchphrase" @ship.name_will_change! - assert_raise(RuntimeError) { assert !@pirate.save } + assert_raise(RuntimeError) { assert_not @pirate.save } assert_not_nil @pirate.reload.ship end @@ -855,7 +855,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase @ship.pirate.catchphrase = "Changed Catchphrase" - assert_raise(RuntimeError) { assert !@ship.save } + assert_raise(RuntimeError) { assert_not @ship.save } assert_not_nil @ship.reload.pirate end @@ -871,7 +871,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase def test_should_destroy_has_many_as_part_of_the_save_transaction_if_they_were_marked_for_destruction 2.times { |i| @pirate.birds.create!(name: "birds_#{i}") } - assert !@pirate.birds.any?(&:marked_for_destruction?) + assert_not @pirate.birds.any?(&:marked_for_destruction?) @pirate.birds.each(&:mark_for_destruction) klass = @pirate.birds.first.class @@ -937,7 +937,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end end - assert_raise(RuntimeError) { assert !@pirate.save } + assert_raise(RuntimeError) { assert_not @pirate.save } assert_equal before, @pirate.reload.birds end @@ -1003,7 +1003,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase def test_should_destroy_habtm_as_part_of_the_save_transaction_if_they_were_marked_for_destruction 2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") } - assert !@pirate.parrots.any?(&:marked_for_destruction?) + assert_not @pirate.parrots.any?(&:marked_for_destruction?) @pirate.parrots.each(&:mark_for_destruction) assert_no_difference "Parrot.count" do @@ -1065,7 +1065,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end end - assert_raise(RuntimeError) { assert !@pirate.save } + assert_raise(RuntimeError) { assert_not @pirate.save } assert_equal before, @pirate.reload.parrots end @@ -1213,7 +1213,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase assert_no_difference "Pirate.count" do assert_no_difference "Ship.count" do - assert !pirate.save + assert_not pirate.save end end end @@ -1232,7 +1232,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase end end - assert_raise(RuntimeError) { assert !@pirate.save } + assert_raise(RuntimeError) { assert_not @pirate.save } assert_equal before, [@pirate.reload.catchphrase, @pirate.ship.name] end @@ -1337,7 +1337,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase assert_no_difference "Ship.count" do assert_no_difference "Pirate.count" do - assert !ship.save + assert_not ship.save end end end @@ -1356,7 +1356,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase end end - assert_raise(RuntimeError) { assert !@ship.save } + assert_raise(RuntimeError) { assert_not @ship.save } assert_equal before, [@ship.pirate.reload.catchphrase, @ship.reload.name] end @@ -1480,7 +1480,7 @@ module AutosaveAssociationOnACollectionAssociationTests @child_1.name = "Changed" @child_1.cancel_save_from_callback = true - assert !@pirate.save + assert_not @pirate.save assert_equal "Don' botharrr talkin' like one, savvy?", @pirate.reload.catchphrase assert_equal "Posideons Killer", @child_1.reload.name @@ -1490,7 +1490,7 @@ module AutosaveAssociationOnACollectionAssociationTests assert_no_difference "Pirate.count" do assert_no_difference "#{new_child.class.name}.count" do - assert !new_pirate.save + assert_not new_pirate.save end end end @@ -1510,7 +1510,7 @@ module AutosaveAssociationOnACollectionAssociationTests end end - assert_raise(RuntimeError) { assert !@pirate.save } + assert_raise(RuntimeError) { assert_not @pirate.save } assert_equal before, [@pirate.reload.catchphrase, *@pirate.send(@association_name).map(&:name)] end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index d5e1c2feb7..fd008ca8e3 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -307,7 +307,7 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "Dude", cbs[0].name assert_equal "Bob", cbs[1].name assert cbs[0].frickinawesome - assert !cbs[1].frickinawesome + assert_not cbs[1].frickinawesome end def test_load @@ -856,11 +856,11 @@ class BasicsTest < ActiveRecord::TestCase def test_clone_of_new_object_marks_as_dirty_only_changed_attributes developer = Developer.new name: "Bjorn" assert developer.name_changed? # obviously - assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed + assert_not developer.salary_changed? # attribute has non-nil default value, so treated as not changed cloned_developer = developer.clone assert_predicate cloned_developer, :name_changed? - assert !cloned_developer.salary_changed? # ... and cloned instance should behave same + assert_not cloned_developer.salary_changed? # ... and cloned instance should behave same end def test_dup_of_saved_object_marks_attributes_as_dirty @@ -875,12 +875,12 @@ class BasicsTest < ActiveRecord::TestCase def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes developer = Developer.create! name: "Bjorn" - assert !developer.name_changed? # both attributes of saved object should be treated as not changed + assert_not developer.name_changed? # both attributes of saved object should be treated as not changed assert_not_predicate developer, :salary_changed? cloned_developer = developer.dup assert cloned_developer.name_changed? # ... but on cloned object should be - assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance + assert_not cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance end def test_bignum diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 3b283a3aa6..b9ba51c730 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -385,9 +385,9 @@ class CallbacksTest < ActiveRecord::TestCase end def assert_save_callbacks_not_called(someone) - assert !someone.after_save_called - assert !someone.after_create_called - assert !someone.after_update_called + assert_not someone.after_save_called + assert_not someone.after_create_called + assert_not someone.after_update_called end private :assert_save_callbacks_not_called @@ -395,27 +395,27 @@ class CallbacksTest < ActiveRecord::TestCase someone = CallbackHaltedDeveloper.new someone.cancel_before_create = true assert_predicate someone, :valid? - assert !someone.save + assert_not someone.save assert_save_callbacks_not_called(someone) end def test_before_save_throwing_abort david = DeveloperWithCanceledCallbacks.find(1) assert_predicate david, :valid? - assert !david.save + assert_not david.save exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! } assert_equal david, exc.record david = DeveloperWithCanceledCallbacks.find(1) david.salary = 10_000_000 assert_not_predicate david, :valid? - assert !david.save + assert_not david.save assert_raise(ActiveRecord::RecordInvalid) { david.save! } someone = CallbackHaltedDeveloper.find(1) someone.cancel_before_save = true assert_predicate someone, :valid? - assert !someone.save + assert_not someone.save assert_save_callbacks_not_called(someone) end @@ -423,22 +423,22 @@ class CallbacksTest < ActiveRecord::TestCase someone = CallbackHaltedDeveloper.find(1) someone.cancel_before_update = true assert_predicate someone, :valid? - assert !someone.save + assert_not someone.save assert_save_callbacks_not_called(someone) end def test_before_destroy_throwing_abort david = DeveloperWithCanceledCallbacks.find(1) - assert !david.destroy + assert_not david.destroy exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! } assert_equal david, exc.record assert_not_nil ImmutableDeveloper.find_by_id(1) someone = CallbackHaltedDeveloper.find(1) someone.cancel_before_destroy = true - assert !someone.destroy + assert_not someone.destroy assert_raise(ActiveRecord::RecordNotDestroyed) { someone.destroy! } - assert !someone.after_destroy_called + assert_not someone.after_destroy_called end def test_callback_throwing_abort @@ -467,12 +467,12 @@ class CallbacksTest < ActiveRecord::TestCase def test_inheritance_of_callbacks parent = ParentDeveloper.new - assert !parent.after_save_called + assert_not parent.after_save_called parent.save assert parent.after_save_called child = ChildDeveloper.new - assert !child.after_save_called + assert_not child.after_save_called child.save assert child.after_save_called end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 5c7f70b7a0..83cc2aa319 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -366,7 +366,7 @@ class DirtyTest < ActiveRecord::TestCase def test_changed_attributes_should_be_preserved_if_save_failure pirate = Pirate.new pirate.parrot_id = 1 - assert !pirate.save + assert_not pirate.save check_pirate_after_save_failure(pirate) pirate = Pirate.new @@ -496,7 +496,7 @@ class DirtyTest < ActiveRecord::TestCase assert_not_nil pirate.previous_changes["updated_on"][1] assert_nil pirate.previous_changes["created_on"][0] assert_not_nil pirate.previous_changes["created_on"][1] - assert !pirate.previous_changes.key?("parrot_id") + assert_not pirate.previous_changes.key?("parrot_id") # original values should be in previous_changes pirate = Pirate.new @@ -510,7 +510,7 @@ class DirtyTest < ActiveRecord::TestCase assert_equal [nil, pirate.id], pirate.previous_changes["id"] assert_includes pirate.previous_changes, "updated_on" assert_includes pirate.previous_changes, "created_on" - assert !pirate.previous_changes.key?("parrot_id") + assert_not pirate.previous_changes.key?("parrot_id") pirate.catchphrase = "Yar!!" pirate.reload @@ -527,8 +527,8 @@ class DirtyTest < ActiveRecord::TestCase assert_equal ["arrr", "Me Maties!"], pirate.previous_changes["catchphrase"] assert_not_nil pirate.previous_changes["updated_on"][0] assert_not_nil pirate.previous_changes["updated_on"][1] - assert !pirate.previous_changes.key?("parrot_id") - assert !pirate.previous_changes.key?("created_on") + assert_not pirate.previous_changes.key?("parrot_id") + assert_not pirate.previous_changes.key?("created_on") pirate = Pirate.find_by_catchphrase("Me Maties!") @@ -541,8 +541,8 @@ class DirtyTest < ActiveRecord::TestCase assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes["catchphrase"] assert_not_nil pirate.previous_changes["updated_on"][0] assert_not_nil pirate.previous_changes["updated_on"][1] - assert !pirate.previous_changes.key?("parrot_id") - assert !pirate.previous_changes.key?("created_on") + assert_not pirate.previous_changes.key?("parrot_id") + assert_not pirate.previous_changes.key?("created_on") travel(1.second) @@ -553,8 +553,8 @@ class DirtyTest < ActiveRecord::TestCase assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes["catchphrase"] assert_not_nil pirate.previous_changes["updated_on"][0] assert_not_nil pirate.previous_changes["updated_on"][1] - assert !pirate.previous_changes.key?("parrot_id") - assert !pirate.previous_changes.key?("created_on") + assert_not pirate.previous_changes.key?("parrot_id") + assert_not pirate.previous_changes.key?("created_on") travel(1.second) @@ -565,8 +565,8 @@ class DirtyTest < ActiveRecord::TestCase assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes["catchphrase"] assert_not_nil pirate.previous_changes["updated_on"][0] assert_not_nil pirate.previous_changes["updated_on"][1] - assert !pirate.previous_changes.key?("parrot_id") - assert !pirate.previous_changes.key?("created_on") + assert_not pirate.previous_changes.key?("parrot_id") + assert_not pirate.previous_changes.key?("created_on") ensure travel_back end diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index 9e33c3110c..387a0b1fdd 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -171,7 +171,7 @@ module ActiveRecord end end - assert !movie.persisted? + assert_not movie.persisted? end end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index d85910d9c6..04150f4d57 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -727,8 +727,8 @@ class FinderTest < ActiveRecord::TestCase assert_raise(ActiveModel::MissingAttributeError) { topic.title? } assert_nil topic.read_attribute("title") assert_equal "David", topic.author_name - assert !topic.attribute_present?("title") - assert !topic.attribute_present?(:title) + assert_not topic.attribute_present?("title") + assert_not topic.attribute_present?(:title) assert topic.attribute_present?("author_name") assert_respond_to topic, "author_name" end diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 184b750161..a4fa3c285b 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -592,10 +592,10 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase fixtures :topics, :developers, :accounts def test_without_complete_instantiation - assert !defined?(@first) - assert !defined?(@topics) - assert !defined?(@developers) - assert !defined?(@accounts) + assert_not defined?(@first) + assert_not defined?(@topics) + assert_not defined?(@developers) + assert_not defined?(@accounts) end def test_fixtures_from_root_yml_without_instantiation @@ -1082,13 +1082,13 @@ class FoxyFixturesTest < ActiveRecord::TestCase def test_supports_inline_habtm assert(parrots(:george).treasures.include?(treasures(:diamond))) assert(parrots(:george).treasures.include?(treasures(:sapphire))) - assert(!parrots(:george).treasures.include?(treasures(:ruby))) + assert_not(parrots(:george).treasures.include?(treasures(:ruby))) end def test_supports_inline_habtm_with_specified_id assert(parrots(:polly).treasures.include?(treasures(:ruby))) assert(parrots(:polly).treasures.include?(treasures(:sapphire))) - assert(!parrots(:polly).treasures.include?(treasures(:diamond))) + assert_not(parrots(:polly).treasures.include?(treasures(:diamond))) end def test_supports_yaml_arrays diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index ebe0b0aa87..9c85543b9b 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -215,7 +215,7 @@ module ActiveRecord migration = InvertibleMigration.new migration.migrate :up migration.migrate :down - assert !migration.connection.table_exists?("horses") + assert_not migration.connection.table_exists?("horses") end def test_migrate_revert @@ -223,11 +223,11 @@ module ActiveRecord revert = InvertibleRevertMigration.new migration.migrate :up revert.migrate :up - assert !migration.connection.table_exists?("horses") + assert_not migration.connection.table_exists?("horses") revert.migrate :down assert migration.connection.table_exists?("horses") migration.migrate :down - assert !migration.connection.table_exists?("horses") + assert_not migration.connection.table_exists?("horses") end def test_migrate_revert_by_part @@ -241,12 +241,12 @@ module ActiveRecord } migration.migrate :up assert_equal [:both, :up], received - assert !migration.connection.table_exists?("horses") + assert_not migration.connection.table_exists?("horses") assert migration.connection.table_exists?("new_horses") migration.migrate :down assert_equal [:both, :up, :both, :down], received assert migration.connection.table_exists?("horses") - assert !migration.connection.table_exists?("new_horses") + assert_not migration.connection.table_exists?("new_horses") end def test_migrate_revert_whole_migration @@ -255,11 +255,11 @@ module ActiveRecord revert = RevertWholeMigration.new(klass) migration.migrate :up revert.migrate :up - assert !migration.connection.table_exists?("horses") + assert_not migration.connection.table_exists?("horses") revert.migrate :down assert migration.connection.table_exists?("horses") migration.migrate :down - assert !migration.connection.table_exists?("horses") + assert_not migration.connection.table_exists?("horses") end end @@ -268,7 +268,7 @@ module ActiveRecord revert.migrate :down assert revert.connection.table_exists?("horses") revert.migrate :up - assert !revert.connection.table_exists?("horses") + assert_not revert.connection.table_exists?("horses") end def test_migrate_revert_change_column_default @@ -402,7 +402,7 @@ module ActiveRecord UpOnlyMigration.new.migrate(:down) # should be no error connection = ActiveRecord::Base.connection - assert !connection.column_exists?(:horses, :oldie) + assert_not connection.column_exists?(:horses, :oldie) Horse.reset_column_information end end diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 1494027182..f4d16cb093 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -205,8 +205,8 @@ module ActiveRecord created_at_column = created_columns.detect { |c| c.name == "created_at" } updated_at_column = created_columns.detect { |c| c.name == "updated_at" } - assert !created_at_column.null - assert !updated_at_column.null + assert_not created_at_column.null + assert_not updated_at_column.null end def test_create_table_with_timestamps_should_create_datetime_columns_with_options @@ -408,7 +408,7 @@ module ActiveRecord end connection.change_table :testings do |t| assert t.column_exists?(:foo) - assert !(t.column_exists?(:bar)) + assert_not (t.column_exists?(:bar)) end end diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb index 83fb4f9385..a1e5fb1115 100644 --- a/activerecord/test/cases/migration/create_join_table_test.rb +++ b/activerecord/test/cases/migration/create_join_table_test.rb @@ -95,42 +95,42 @@ module ActiveRecord connection.create_join_table :artists, :musics connection.drop_join_table :artists, :musics - assert !connection.table_exists?("artists_musics") + assert_not connection.table_exists?("artists_musics") end def test_drop_join_table_with_strings connection.create_join_table :artists, :musics connection.drop_join_table "artists", "musics" - assert !connection.table_exists?("artists_musics") + assert_not connection.table_exists?("artists_musics") end def test_drop_join_table_with_the_proper_order connection.create_join_table :videos, :musics connection.drop_join_table :videos, :musics - assert !connection.table_exists?("musics_videos") + assert_not connection.table_exists?("musics_videos") end def test_drop_join_table_with_the_table_name connection.create_join_table :artists, :musics, table_name: :catalog connection.drop_join_table :artists, :musics, table_name: :catalog - assert !connection.table_exists?("catalog") + assert_not connection.table_exists?("catalog") end def test_drop_join_table_with_the_table_name_as_string connection.create_join_table :artists, :musics, table_name: "catalog" connection.drop_join_table :artists, :musics, table_name: "catalog" - assert !connection.table_exists?("catalog") + assert_not connection.table_exists?("catalog") end def test_drop_join_table_with_column_options connection.create_join_table :artists, :musics, column_options: { null: true } connection.drop_join_table :artists, :musics, column_options: { null: true } - assert !connection.table_exists?("artists_musics") + assert_not connection.table_exists?("artists_musics") end def test_create_and_drop_join_table_with_common_prefix diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb index b25c6d84bc..f9b2dc0c73 100644 --- a/activerecord/test/cases/migration/index_test.rb +++ b/activerecord/test/cases/migration/index_test.rb @@ -99,7 +99,7 @@ module ActiveRecord connection.add_index :testings, :foo assert connection.index_exists?(:testings, :foo) - assert !connection.index_exists?(:testings, :bar) + assert_not connection.index_exists?(:testings, :bar) end def test_index_exists_on_multiple_columns @@ -131,7 +131,7 @@ module ActiveRecord assert connection.index_exists?(:testings, :foo) assert connection.index_exists?(:testings, :foo, name: "custom_index_name") - assert !connection.index_exists?(:testings, :foo, name: "other_index_name") + assert_not connection.index_exists?(:testings, :foo, name: "other_index_name") end def test_remove_named_index @@ -139,7 +139,7 @@ module ActiveRecord assert connection.index_exists?(:testings, :foo) connection.remove_index :testings, :foo - assert !connection.index_exists?(:testings, :foo) + assert_not connection.index_exists?(:testings, :foo) end def test_add_index_attribute_length_limit @@ -203,7 +203,7 @@ module ActiveRecord assert connection.index_exists?("testings", "last_name") connection.remove_index("testings", "last_name") - assert !connection.index_exists?("testings", "last_name") + assert_not connection.index_exists?("testings", "last_name") end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index e3fd7d1a7b..1fa9a3c34a 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -548,7 +548,7 @@ class MigrationTest < ActiveRecord::TestCase end assert Person.connection.column_exists?(:something, :foo) assert_nothing_raised { Person.connection.remove_column :something, :foo, :bar } - assert !Person.connection.column_exists?(:something, :foo) + assert_not Person.connection.column_exists?(:something, :foo) assert Person.connection.column_exists?(:something, :name) assert Person.connection.column_exists?(:something, :number) ensure @@ -822,7 +822,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter? end end - [:qualification, :experience].each { |c| assert ! column(c) } + [:qualification, :experience].each { |c| assert_not column(c) } assert column(:qualification_experience) end @@ -852,7 +852,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter? name_age_index = index(:index_delete_me_on_name_and_age) assert_equal ["name", "age"].sort, name_age_index.columns.sort - assert ! name_age_index.unique + assert_not name_age_index.unique assert index(:awesome_username_index).unique end @@ -880,7 +880,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter? end end - assert ! index(:index_delete_me_on_name) + assert_not index(:index_delete_me_on_name) new_name_index = index(:new_name_index) assert new_name_index.unique @@ -892,7 +892,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter? t.date :birthdate end - assert ! column(:name).default + assert_not column(:name).default assert_equal :date, column(:birthdate).type classname = ActiveRecord::Base.connection.class.name[/[^:]*$/] diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 1ed3a61bbb..32af90caef 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -83,7 +83,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction ship = Ship.create!(name: "Nights Dirty Lightning") - assert !ship._destroy + assert_not ship._destroy ship.mark_for_destruction assert ship._destroy end @@ -835,7 +835,7 @@ module NestedAttributesOnACollectionAssociationTests man = Man.create(name: "John") interest = man.interests.create(topic: "bar", zine_id: 0) assert interest.save - assert !man.update(interests_attributes: { id: interest.id, zine_id: "foo" }) + assert_not man.update(interests_attributes: { id: interest.id, zine_id: "foo" }) end end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index e4a65a48ca..e0c5725944 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -48,7 +48,7 @@ class PersistenceTest < ActiveRecord::TestCase end if test_update_with_order_succeeds.call("id DESC") - assert !test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception + assert_not test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception else # test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\z/i) do diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 07be046fb7..393f363e37 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -518,19 +518,19 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase def test_find assert_called(Task.connection, :clear_query_cache) do - assert !Task.connection.query_cache_enabled + assert_not Task.connection.query_cache_enabled Task.cache do assert Task.connection.query_cache_enabled Task.find(1) Task.uncached do - assert !Task.connection.query_cache_enabled + assert_not Task.connection.query_cache_enabled Task.find(1) end assert Task.connection.query_cache_enabled end - assert !Task.connection.query_cache_enabled + assert_not Task.connection.query_cache_enabled end end diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb index 383e43ed55..059fa76132 100644 --- a/activerecord/test/cases/readonly_test.rb +++ b/activerecord/test/cases/readonly_test.rb @@ -23,7 +23,7 @@ class ReadOnlyTest < ActiveRecord::TestCase assert_nothing_raised do dev.name = "Luscious forbidden fruit." - assert !dev.save + assert_not dev.save dev.name = "Forbidden." end @@ -38,8 +38,8 @@ class ReadOnlyTest < ActiveRecord::TestCase end def test_find_with_readonly_option - Developer.all.each { |d| assert !d.readonly? } - Developer.readonly(false).each { |d| assert !d.readonly? } + Developer.all.each { |d| assert_not d.readonly? } + Developer.readonly(false).each { |d| assert_not d.readonly? } Developer.readonly(true).each { |d| assert d.readonly? } Developer.readonly.each { |d| assert d.readonly? } end @@ -55,14 +55,14 @@ class ReadOnlyTest < ActiveRecord::TestCase def test_has_many_find_readonly post = Post.find(1) assert_not_empty post.comments - assert !post.comments.any?(&:readonly?) - assert !post.comments.to_a.any?(&:readonly?) + assert_not post.comments.any?(&:readonly?) + assert_not post.comments.to_a.any?(&:readonly?) assert post.comments.readonly(true).all?(&:readonly?) end def test_has_many_with_through_is_not_implicitly_marked_readonly assert people = Post.find(1).people - assert !people.any?(&:readonly?) + assert_not people.any?(&:readonly?) end def test_has_many_with_through_is_not_implicitly_marked_readonly_while_finding_by_id diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb index 61cb0f130d..b034fe3e3b 100644 --- a/activerecord/test/cases/reaper_test.rb +++ b/activerecord/test/cases/reaper_test.rb @@ -36,15 +36,15 @@ module ActiveRecord # A reaper with nil time should never reap connections def test_nil_time fp = FakePool.new - assert !fp.reaped + assert_not fp.reaped reaper = ConnectionPool::Reaper.new(fp, nil) reaper.run - assert !fp.reaped + assert_not fp.reaped end def test_some_time fp = FakePool.new - assert !fp.reaped + assert_not fp.reaped reaper = ConnectionPool::Reaper.new(fp, 0.0001) reaper.run diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index ed19192ad9..abadafbad4 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -75,7 +75,7 @@ class ReflectionTest < ActiveRecord::TestCase def test_column_null_not_null subscriber = Subscriber.first assert subscriber.column_for_attribute("name").null - assert !subscriber.column_for_attribute("nick").null + assert_not subscriber.column_for_attribute("nick").null end def test_human_name_for_column diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 074ce9454f..f53ef1fe35 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -78,6 +78,10 @@ class RelationMergingTest < ActiveRecord::TestCase assert_equal 1, comments.count end + def test_relation_merging_with_skip_query_cache + assert_equal Post.all.merge(Post.all.skip_query_cache!).skip_query_cache_value, true + end + def test_relation_merging_with_association assert_queries(2) do # one for loading post, and another one merged query post = Post.where(body: "Such a lovely day").first diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index cf65789b97..63edd82f7f 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -511,7 +511,7 @@ class RelationTest < ActiveRecord::TestCase end def test_find_with_readonly_option - Developer.all.each { |d| assert !d.readonly? } + Developer.all.each { |d| assert_not d.readonly? } Developer.all.readonly.each { |d| assert d.readonly? } end @@ -1097,7 +1097,7 @@ class RelationTest < ActiveRecord::TestCase assert_not_predicate posts.where(id: nil), :any? assert posts.any? { |p| p.id > 0 } - assert ! posts.any? { |p| p.id <= 0 } + assert_not posts.any? { |p| p.id <= 0 } end assert_predicate posts, :loaded? @@ -1109,7 +1109,7 @@ class RelationTest < ActiveRecord::TestCase assert_queries(2) do assert posts.many? # Uses COUNT() assert posts.many? { |p| p.id > 0 } - assert ! posts.many? { |p| p.id < 2 } + assert_not posts.many? { |p| p.id < 2 } end assert_predicate posts, :loaded? @@ -1125,14 +1125,14 @@ class RelationTest < ActiveRecord::TestCase def test_none? posts = Post.all assert_queries(1) do - assert ! posts.none? # Uses COUNT() + assert_not posts.none? # Uses COUNT() end assert_not_predicate posts, :loaded? assert_queries(1) do assert posts.none? { |p| p.id < 0 } - assert ! posts.none? { |p| p.id == 1 } + assert_not posts.none? { |p| p.id == 1 } end assert_predicate posts, :loaded? @@ -1141,13 +1141,13 @@ class RelationTest < ActiveRecord::TestCase def test_one posts = Post.all assert_queries(1) do - assert ! posts.one? # Uses COUNT() + assert_not posts.one? # Uses COUNT() end assert_not_predicate posts, :loaded? assert_queries(1) do - assert ! posts.one? { |p| p.id < 3 } + assert_not posts.one? { |p| p.id < 3 } assert posts.one? { |p| p.id == 1 } end @@ -1696,7 +1696,7 @@ class RelationTest < ActiveRecord::TestCase # checking if there are topics is used before you actually display them, # thus it shouldn't invoke an extra count query. assert_no_queries { assert topics.present? } - assert_no_queries { assert !topics.blank? } + assert_no_queries { assert_not topics.blank? } # shows count of topics and loops after loading the query should not trigger extra queries either. assert_no_queries { topics.size } diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 0804de1fb3..e3a34aa50d 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -193,7 +193,7 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_order_to_unscope_reordering scope = DeveloperOrderedBySalary.order("salary DESC, name ASC").reverse_order.unscope(:order) - assert !/order/i.match?(scope.to_sql) + assert_no_match(/order/i, scope.to_sql) end def test_unscope_reverse_order diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index 03f8a4f7c9..6cb252edaf 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -86,7 +86,7 @@ class NamedScopingTest < ActiveRecord::TestCase def test_scopes_are_composable assert_equal((approved = Topic.all.merge!(where: { approved: true }).to_a), Topic.approved) assert_equal((replied = Topic.all.merge!(where: "replies_count > 0").to_a), Topic.replied) - assert !(approved == replied) + assert_not (approved == replied) assert_not_empty (approved & replied) assert_equal approved & replied, Topic.approved.replied diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index 5c86bc892d..f18f1ed981 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -105,7 +105,7 @@ class RelationScopingTest < ActiveRecord::TestCase Developer.select("id, name").scoping do developer = Developer.where("name = 'David'").first assert_equal "David", developer.name - assert !developer.has_attribute?(:salary) + assert_not developer.has_attribute?(:salary) end end diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb index 2d829ad4ba..932780bfef 100644 --- a/activerecord/test/cases/serialization_test.rb +++ b/activerecord/test/cases/serialization_test.rb @@ -67,8 +67,8 @@ class SerializationTest < ActiveRecord::TestCase klazz.include_root_in_json = false assert ActiveRecord::Base.include_root_in_json - assert !klazz.include_root_in_json - assert !klazz.new.include_root_in_json + assert_not klazz.include_root_in_json + assert_not klazz.new.include_root_in_json ensure ActiveRecord::Base.include_root_in_json = original_root_in_json end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 1c144a781d..3fd38b4b60 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -159,7 +159,7 @@ class TransactionTest < ActiveRecord::TestCase def @first.before_save_for_transaction raise ActiveRecord::Rollback end - assert !@first.approved + assert_not @first.approved Topic.transaction do @first.approved = true @@ -194,7 +194,7 @@ class TransactionTest < ActiveRecord::TestCase posts_count = author.posts.size assert posts_count > 0 status = author.update(name: nil, post_ids: []) - assert !status + assert_not status assert_equal posts_count, author.posts.reload.size end @@ -212,7 +212,7 @@ class TransactionTest < ActiveRecord::TestCase add_cancelling_before_destroy_with_db_side_effect_to_topic @first nbooks_before_destroy = Book.count status = @first.destroy - assert !status + assert_not status @first.reload assert_equal nbooks_before_destroy, Book.count end @@ -224,7 +224,7 @@ class TransactionTest < ActiveRecord::TestCase original_author_name = @first.author_name @first.author_name += "_this_should_not_end_up_in_the_db" status = @first.save - assert !status + assert_not status assert_equal original_author_name, @first.reload.author_name assert_equal nbooks_before_save, Book.count end diff --git a/activerecord/test/cases/validations/length_validation_test.rb b/activerecord/test/cases/validations/length_validation_test.rb index 62cd89041a..1fbcdc271b 100644 --- a/activerecord/test/cases/validations/length_validation_test.rb +++ b/activerecord/test/cases/validations/length_validation_test.rb @@ -17,7 +17,7 @@ class LengthValidationTest < ActiveRecord::TestCase def test_validates_size_of_association assert_nothing_raised { @owner.validates_size_of :pets, minimum: 1 } o = @owner.new("name" => "nopets") - assert !o.save + assert_not o.save assert_predicate o.errors[:pets], :any? o.pets.build("name" => "apet") assert_predicate o, :valid? @@ -26,21 +26,21 @@ class LengthValidationTest < ActiveRecord::TestCase def test_validates_size_of_association_using_within assert_nothing_raised { @owner.validates_size_of :pets, within: 1..2 } o = @owner.new("name" => "nopets") - assert !o.save + assert_not o.save assert_predicate o.errors[:pets], :any? o.pets.build("name" => "apet") assert_predicate o, :valid? 2.times { o.pets.build("name" => "apet") } - assert !o.save + assert_not o.save assert_predicate o.errors[:pets], :any? end def test_validates_size_of_association_utf8 @owner.validates_size_of :pets, minimum: 1 o = @owner.new("name" => "あいうえおかきくけこ") - assert !o.save + assert_not o.save assert_predicate o.errors[:pets], :any? o.pets.build("name" => "あいうえおかきくけこ") assert_predicate o, :valid? diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 6c83bbd15c..a33877f43a 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -39,7 +39,7 @@ class ValidationsTest < ActiveRecord::TestCase def test_valid_using_special_context r = WrongReply.new(title: "Valid title") - assert !r.valid?(:special_case) + assert_not r.valid?(:special_case) assert_equal "Invalid", r.errors[:author_name].join r.author_name = "secret" @@ -125,7 +125,7 @@ class ValidationsTest < ActiveRecord::TestCase def test_save_without_validation reply = WrongReply.new - assert !reply.save + assert_not reply.save assert reply.save(validate: false) end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 82c985fae2..3cbaa65dea 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,13 @@ +* Fix bug where `ActiveSupport::Timezone.all` would fail when tzinfo data for + any timezone defined in `ActiveSupport::MAPPING` is missing. + + *Dominik Sander* + +* Redis cache store: `delete_matched` no longer blocks the Redis server. + (Switches from evaled Lua to a batched SCAN + DEL loop.) + + *Gleb Mazovetskiy* + * Fix bug where `ActiveSupport::Cache` will massively inflate the storage size when compression is enabled (which is true by default). This patch does not attempt to repair existing data: please manually flush the cache @@ -18,6 +28,34 @@ *Nick Holden* +* `ActiveSupport::Inflector#ordinal` and `ActiveSupport::Inflector#ordinalize` now support + translations through I18n. + + # locale/fr.rb + + { + fr: { + number: { + nth: { + ordinals: lambda do |_key, number:, **_options| + if number.to_i.abs == 1 + 'er' + else + 'e' + end + end, + + ordinalized: lambda do |_key, number:, **_options| + "#{number}#{ActiveSupport::Inflector.ordinal(number)}" + end + } + } + } + } + + + *Christian Blais* + * Add `:private` option to ActiveSupport's `Module#delegate` in order to delegate methods as private: diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb index a1cb6db25d..11c574258f 100644 --- a/activesupport/lib/active_support/cache/redis_cache_store.rb +++ b/activesupport/lib/active_support/cache/redis_cache_store.rb @@ -62,8 +62,9 @@ module ActiveSupport end end - DELETE_GLOB_LUA = "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" - private_constant :DELETE_GLOB_LUA + # The maximum number of entries to receive per SCAN call. + SCAN_BATCH_SIZE = 1000 + private_constant :SCAN_BATCH_SIZE # Support raw values in the local cache strategy. module LocalCacheWithRaw # :nodoc: @@ -231,12 +232,18 @@ module ActiveSupport # Failsafe: Raises errors. def delete_matched(matcher, options = nil) instrument :delete_matched, matcher do - case matcher - when String - redis.with { |c| c.eval DELETE_GLOB_LUA, [], [namespace_key(matcher, options)] } - else + unless String === matcher raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}" end + redis.with do |c| + pattern = namespace_key(matcher, options) + cursor = "0" + # Fetch keys in batches using SCAN to avoid blocking the Redis server. + begin + cursor, keys = c.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE) + c.del(*keys) unless keys.empty? + end until cursor == "0" + end end end @@ -286,7 +293,7 @@ module ActiveSupport # Failsafe: Raises errors. def clear(options = nil) failsafe :clear do - if namespace = merged_options(options)[namespace] + if namespace = merged_options(options)[:namespace] delete_matched "*", namespace: namespace else redis.with { |c| c.flushdb } diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 580baffa2b..01fee0fb74 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -163,10 +163,10 @@ class Module # parent class. Similarly if parent class changes the value then that would # change the value of subclasses too. # - # class Male < Person + # class Citizen < Person # end # - # Male.new.hair_colors << :blue + # Citizen.new.hair_colors << :blue # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue] # # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>. diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index 2c004f4c9e..7075b5b869 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -104,7 +104,7 @@ module ActiveSupport end end - RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" def ignored_callstack(path) path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"]) diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index d60b3eff30..39dab1cc71 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -13,3 +13,4 @@ require "active_support/lazy_load_hooks" ActiveSupport.run_load_hooks(:i18n) I18n.load_path << File.expand_path("locale/en.yml", __dir__) +I18n.load_path << File.expand_path("locale/en.rb", __dir__) diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 7e782e2a93..339b93b8da 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -341,18 +341,7 @@ module ActiveSupport # ordinal(-11) # => "th" # ordinal(-1021) # => "st" def ordinal(number) - abs_number = number.to_i.abs - - if (11..13).include?(abs_number % 100) - "th" - else - case abs_number % 10 - when 1; "st" - when 2; "nd" - when 3; "rd" - else "th" - end - end + I18n.translate("number.nth.ordinals", number: number) end # Turns a number into an ordinal string used to denote the position in an @@ -365,7 +354,7 @@ module ActiveSupport # ordinalize(-11) # => "-11th" # ordinalize(-1021) # => "-1021st" def ordinalize(number) - "#{number}#{ordinal(number)}" + I18n.translate("number.nth.ordinalized", number: number) end private diff --git a/activesupport/lib/active_support/locale/en.rb b/activesupport/lib/active_support/locale/en.rb new file mode 100644 index 0000000000..26c2280c95 --- /dev/null +++ b/activesupport/lib/active_support/locale/en.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +{ + en: { + number: { + nth: { + ordinals: lambda do |_key, number:, **_options| + abs_number = number.to_i.abs + + if (11..13).cover?(abs_number % 100) + "th" + else + case abs_number % 10 + when 1 then "st" + when 2 then "nd" + when 3 then "rd" + else "th" + end + end + end, + + ordinalized: lambda do |_key, number:, **_options| + "#{number}#{ActiveSupport::Inflector.ordinal(number)}" + end + } + } + } +} diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 9dfaddb825..5f709c5fd9 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -279,7 +279,8 @@ module ActiveSupport def zones_map @zones_map ||= MAPPING.each_with_object({}) do |(name, _), zones| - zones[name] = self[name] + timezone = self[name] + zones[name] = timezone if timezone end end end diff --git a/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb b/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb index 6f59ce48d2..ed8eba8fc2 100644 --- a/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb @@ -7,9 +7,9 @@ module CacheDeleteMatchedBehavior @cache.write("foo/bar", "baz") @cache.write("fu/baz", "bar") @cache.delete_matched(/oo/) - assert !@cache.exist?("foo") + assert_not @cache.exist?("foo") assert @cache.exist?("fu") - assert !@cache.exist?("foo/bar") + assert_not @cache.exist?("foo/bar") assert @cache.exist?("fu/baz") end end diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb index e2146a1b3a..f9153ffe2a 100644 --- a/activesupport/test/cache/behaviors/cache_store_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb @@ -33,7 +33,7 @@ module CacheStoreBehavior cache_miss = false assert_equal 3, @cache.fetch("foo") { |key| cache_miss = true; key.length } - assert !cache_miss + assert_not cache_miss end def test_fetch_with_forced_cache_miss @@ -308,7 +308,7 @@ module CacheStoreBehavior @cache.write("foo", "bar") assert @cache.exist?("foo") assert @cache.delete("foo") - assert !@cache.exist?("foo") + assert_not @cache.exist?("foo") end def test_original_store_objects_should_not_be_immutable @@ -454,8 +454,8 @@ module CacheStoreBehavior def assert_compression(should_compress, value, **options) freeze_time do - @cache.write("actual", value, **options) - @cache.write("uncompressed", value, **options, compress: false) + @cache.write("actual", value, options) + @cache.write("uncompressed", value, options.merge(compress: false)) end if value.nil? diff --git a/activesupport/test/cache/behaviors/cache_store_version_behavior.rb b/activesupport/test/cache/behaviors/cache_store_version_behavior.rb index 62c0bb4f6f..805f061839 100644 --- a/activesupport/test/cache/behaviors/cache_store_version_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_store_version_behavior.rb @@ -30,7 +30,7 @@ module CacheStoreVersionBehavior def test_exist_with_wrong_version_should_be_false @cache.write("foo", "bar", version: 1) - assert !@cache.exist?("foo", version: 2) + assert_not @cache.exist?("foo", version: 2) end def test_reading_and_writing_with_model_supporting_cache_version diff --git a/activesupport/test/cache/behaviors/failure_safety_behavior.rb b/activesupport/test/cache/behaviors/failure_safety_behavior.rb index 53bda4f942..43b67d81db 100644 --- a/activesupport/test/cache/behaviors/failure_safety_behavior.rb +++ b/activesupport/test/cache/behaviors/failure_safety_behavior.rb @@ -63,7 +63,7 @@ module FailureSafetyBehavior @cache.write("foo", "bar") emulating_unavailability do |cache| - assert !cache.exist?("foo") + assert_not cache.exist?("foo") end end diff --git a/activesupport/test/cache/cache_store_namespace_test.rb b/activesupport/test/cache/cache_store_namespace_test.rb index b52a61c500..dfdb3262f2 100644 --- a/activesupport/test/cache/cache_store_namespace_test.rb +++ b/activesupport/test/cache/cache_store_namespace_test.rb @@ -25,7 +25,7 @@ class CacheStoreNamespaceTest < ActiveSupport::TestCase cache.write("foo", "bar") cache.write("fu", "baz") cache.delete_matched(/^fo/) - assert !cache.exist?("foo") + assert_not cache.exist?("foo") assert cache.exist?("fu") end @@ -34,7 +34,7 @@ class CacheStoreNamespaceTest < ActiveSupport::TestCase cache.write("foo", "bar") cache.write("fu", "baz") cache.delete_matched(/OO/i) - assert !cache.exist?("foo") + assert_not cache.exist?("foo") assert cache.exist?("fu") end end diff --git a/activesupport/test/cache/stores/memory_store_test.rb b/activesupport/test/cache/stores/memory_store_test.rb index 8fe8384fb0..340fb517cb 100644 --- a/activesupport/test/cache/stores/memory_store_test.rb +++ b/activesupport/test/cache/stores/memory_store_test.rb @@ -104,7 +104,7 @@ class MemoryStorePruningTest < ActiveSupport::TestCase assert @cache.exist?(4) assert @cache.exist?(3) assert @cache.exist?(2) - assert !@cache.exist?(1) + assert_not @cache.exist?(1) end def test_write_with_unless_exist diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb index dda96b68fb..24c4c5c481 100644 --- a/activesupport/test/cache/stores/redis_cache_store_test.rb +++ b/activesupport/test/cache/stores/redis_cache_store_test.rb @@ -211,7 +211,7 @@ module ActiveSupport::Cache::RedisCacheStoreTests @cache.write("foo", "bar") @cache.write("fu", "baz") @cache.delete_matched("foo*") - assert !@cache.exist?("foo") + assert_not @cache.exist?("foo") assert @cache.exist?("fu") end @@ -221,4 +221,22 @@ module ActiveSupport::Cache::RedisCacheStoreTests end end end + + class ClearTest < StoreTest + test "clear all cache key" do + @cache.write("foo", "bar") + @cache.write("fu", "baz") + @cache.clear + assert_not @cache.exist?("foo") + assert_not @cache.exist?("fu") + end + + test "only clear namespace cache key" do + @cache.write("foo", "bar") + @cache.redis.set("fu", "baz") + @cache.clear + assert_not @cache.exist?("foo") + assert @cache.redis.exists("fu") + end + end end diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 6c7643ed72..5c9a3b29e7 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -829,7 +829,7 @@ module CallbacksTest def test_block_never_called_if_terminated obj = CallbackTerminator.new obj.save - assert !obj.saved + assert_not obj.saved end end @@ -857,7 +857,7 @@ module CallbacksTest def test_block_never_called_if_abort_is_thrown obj = CallbackDefaultTerminator.new obj.save - assert !obj.saved + assert_not obj.saved end end diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb index 8cfcaedafd..1ef1939b4b 100644 --- a/activesupport/test/class_cache_test.rb +++ b/activesupport/test/class_cache_test.rb @@ -68,7 +68,7 @@ module ActiveSupport def test_new_rejects_strings @cache.store ClassCacheTest.name - assert !@cache.key?(ClassCacheTest.name) + assert_not @cache.key?(ClassCacheTest.name) end def test_store_returns_self diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 8f6befe809..240ae3bde0 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -16,30 +16,30 @@ class DurationTest < ActiveSupport::TestCase assert_kind_of ActiveSupport::Duration, d assert_kind_of Numeric, d assert_kind_of Integer, d - assert !d.is_a?(Hash) + assert_not d.is_a?(Hash) k = Class.new class << k; undef_method :== end - assert !d.is_a?(k) + assert_not d.is_a?(k) end def test_instance_of assert 1.minute.instance_of?(Integer) assert 2.days.instance_of?(ActiveSupport::Duration) - assert !3.second.instance_of?(Numeric) + assert_not 3.second.instance_of?(Numeric) end def test_threequals assert ActiveSupport::Duration === 1.day - assert !(ActiveSupport::Duration === 1.day.to_i) - assert !(ActiveSupport::Duration === "foo") + assert_not (ActiveSupport::Duration === 1.day.to_i) + assert_not (ActiveSupport::Duration === "foo") end def test_equals assert 1.day == 1.day assert 1.day == 1.day.to_i assert 1.day.to_i == 1.day - assert !(1.day == "foo") + assert_not (1.day == "foo") end def test_to_s @@ -53,11 +53,11 @@ class DurationTest < ActiveSupport::TestCase assert 1.minute.eql?(1.minute) assert 1.minute.eql?(60.seconds) assert 2.days.eql?(48.hours) - assert !1.second.eql?(1) - assert !1.eql?(1.second) + assert_not 1.second.eql?(1) + assert_not 1.eql?(1.second) assert 1.minute.eql?(180.seconds - 2.minutes) - assert !1.minute.eql?(60) - assert !1.minute.eql?("foo") + assert_not 1.minute.eql?(60) + assert_not 1.minute.eql?("foo") end def test_inspect diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb index 23e3c277cc..9c97700e5d 100644 --- a/activesupport/test/core_ext/file_test.rb +++ b/activesupport/test/core_ext/file_test.rb @@ -8,7 +8,7 @@ class AtomicWriteTest < ActiveSupport::TestCase contents = "Atomic Text" File.atomic_write(file_name, Dir.pwd) do |file| file.write(contents) - assert !File.exist?(file_name) + assert_not File.exist?(file_name) end assert File.exist?(file_name) assert_equal contents, File.read(file_name) @@ -22,7 +22,7 @@ class AtomicWriteTest < ActiveSupport::TestCase raise "something bad" end rescue - assert !File.exist?(file_name) + assert_not File.exist?(file_name) end def test_atomic_write_preserves_file_permissions @@ -50,7 +50,7 @@ class AtomicWriteTest < ActiveSupport::TestCase contents = "Atomic Text" File.atomic_write(file_name, Dir.pwd) do |file| file.write(contents) - assert !File.exist?(file_name) + assert_not File.exist?(file_name) end assert File.exist?(file_name) assert_equal File.probe_stat_in(Dir.pwd).mode, file_mode diff --git a/activesupport/test/core_ext/integer_ext_test.rb b/activesupport/test/core_ext/integer_ext_test.rb index 14169b084d..5691dc5341 100644 --- a/activesupport/test/core_ext/integer_ext_test.rb +++ b/activesupport/test/core_ext/integer_ext_test.rb @@ -8,14 +8,14 @@ class IntegerExtTest < ActiveSupport::TestCase def test_multiple_of [ -7, 0, 7, 14 ].each { |i| assert i.multiple_of?(7) } - [ -7, 7, 14 ].each { |i| assert ! i.multiple_of?(6) } + [ -7, 7, 14 ].each { |i| assert_not i.multiple_of?(6) } # test the 0 edge case assert 0.multiple_of?(0) - assert !5.multiple_of?(0) + assert_not 5.multiple_of?(0) # test with a prime - [2, 3, 5, 7].each { |i| assert !PRIME.multiple_of?(i) } + [2, 3, 5, 7].each { |i| assert_not PRIME.multiple_of?(i) } end def test_ordinalize diff --git a/activesupport/test/core_ext/module/attr_internal_test.rb b/activesupport/test/core_ext/module/attr_internal_test.rb index c2a28eced4..9a65f75497 100644 --- a/activesupport/test/core_ext/module/attr_internal_test.rb +++ b/activesupport/test/core_ext/module/attr_internal_test.rb @@ -12,7 +12,7 @@ class AttrInternalTest < ActiveSupport::TestCase def test_reader assert_nothing_raised { @target.attr_internal_reader :foo } - assert !@instance.instance_variable_defined?("@_foo") + assert_not @instance.instance_variable_defined?("@_foo") assert_raise(NoMethodError) { @instance.foo = 1 } @instance.instance_variable_set("@_foo", 1) @@ -22,7 +22,7 @@ class AttrInternalTest < ActiveSupport::TestCase def test_writer assert_nothing_raised { @target.attr_internal_writer :foo } - assert !@instance.instance_variable_defined?("@_foo") + assert_not @instance.instance_variable_defined?("@_foo") assert_nothing_raised { assert_equal 1, @instance.foo = 1 } assert_equal 1, @instance.instance_variable_get("@_foo") @@ -32,7 +32,7 @@ class AttrInternalTest < ActiveSupport::TestCase def test_accessor assert_nothing_raised { @target.attr_internal :foo } - assert !@instance.instance_variable_defined?("@_foo") + assert_not @instance.instance_variable_defined?("@_foo") assert_nothing_raised { assert_equal 1, @instance.foo = 1 } assert_equal 1, @instance.instance_variable_get("@_foo") @@ -44,10 +44,10 @@ class AttrInternalTest < ActiveSupport::TestCase assert_nothing_raised { Module.attr_internal_naming_format = "@abc%sdef" } @target.attr_internal :foo - assert !@instance.instance_variable_defined?("@_foo") - assert !@instance.instance_variable_defined?("@abcfoodef") + assert_not @instance.instance_variable_defined?("@_foo") + assert_not @instance.instance_variable_defined?("@abcfoodef") assert_nothing_raised { @instance.foo = 1 } - assert !@instance.instance_variable_defined?("@_foo") + assert_not @instance.instance_variable_defined?("@_foo") assert @instance.instance_variable_defined?("@abcfoodef") ensure Module.attr_internal_naming_format = "@_%s" diff --git a/activesupport/test/core_ext/module/concerning_test.rb b/activesupport/test/core_ext/module/concerning_test.rb index 969434766f..374114c11b 100644 --- a/activesupport/test/core_ext/module/concerning_test.rb +++ b/activesupport/test/core_ext/module/concerning_test.rb @@ -21,7 +21,7 @@ class ModuleConcernTest < ActiveSupport::TestCase # Declares a concern but doesn't include it assert klass.const_defined?(:Baz, false) - assert !ModuleConcernTest.const_defined?(:Baz) + assert_not ModuleConcernTest.const_defined?(:Baz) assert_kind_of ActiveSupport::Concern, klass::Baz assert_not_includes klass.ancestors, klass::Baz, klass.ancestors.inspect diff --git a/activesupport/test/core_ext/name_error_test.rb b/activesupport/test/core_ext/name_error_test.rb index d1dace3713..5c6c12ffc7 100644 --- a/activesupport/test/core_ext/name_error_test.rb +++ b/activesupport/test/core_ext/name_error_test.rb @@ -17,7 +17,7 @@ class NameErrorTest < ActiveSupport::TestCase exc = assert_raise NameError do some_method_that_does_not_exist end - assert !exc.missing_name?(:Foo) + assert_not exc.missing_name?(:Foo) assert_nil exc.missing_name end end diff --git a/activesupport/test/core_ext/object/acts_like_test.rb b/activesupport/test/core_ext/object/acts_like_test.rb index 9f7b81f7fc..31241caf0a 100644 --- a/activesupport/test/core_ext/object/acts_like_test.rb +++ b/activesupport/test/core_ext/object/acts_like_test.rb @@ -17,19 +17,19 @@ class ObjectTests < ActiveSupport::TestCase dt = DateTime.new duck = DuckTime.new - assert !object.acts_like?(:time) - assert !object.acts_like?(:date) + assert_not object.acts_like?(:time) + assert_not object.acts_like?(:date) assert time.acts_like?(:time) - assert !time.acts_like?(:date) + assert_not time.acts_like?(:date) - assert !date.acts_like?(:time) + assert_not date.acts_like?(:time) assert date.acts_like?(:date) assert dt.acts_like?(:time) assert dt.acts_like?(:date) assert duck.acts_like?(:time) - assert !duck.acts_like?(:date) + assert_not duck.acts_like?(:date) end end diff --git a/activesupport/test/core_ext/object/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb index 2486592441..1fb26ebac7 100644 --- a/activesupport/test/core_ext/object/deep_dup_test.rb +++ b/activesupport/test/core_ext/object/deep_dup_test.rb @@ -47,7 +47,7 @@ class DeepDupTest < ActiveSupport::TestCase object = Object.new dup = object.deep_dup dup.instance_variable_set(:@a, 1) - assert !object.instance_variable_defined?(:@a) + assert_not object.instance_variable_defined?(:@a) assert dup.instance_variable_defined?(:@a) end diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb index 52c21f2e8e..8cbb4f848f 100644 --- a/activesupport/test/core_ext/object/inclusion_test.rb +++ b/activesupport/test/core_ext/object/inclusion_test.rb @@ -6,30 +6,30 @@ require "active_support/core_ext/object/inclusion" class InTest < ActiveSupport::TestCase def test_in_array assert 1.in?([1, 2]) - assert !3.in?([1, 2]) + assert_not 3.in?([1, 2]) end def test_in_hash h = { "a" => 100, "b" => 200 } assert "a".in?(h) - assert !"z".in?(h) + assert_not "z".in?(h) end def test_in_string assert "lo".in?("hello") - assert !"ol".in?("hello") + assert_not "ol".in?("hello") assert ?h.in?("hello") end def test_in_range assert 25.in?(1..50) - assert !75.in?(1..50) + assert_not 75.in?(1..50) end def test_in_set s = Set.new([1, 2]) assert 1.in?(s) - assert !3.in?(s) + assert_not 3.in?(s) end module A @@ -45,8 +45,8 @@ class InTest < ActiveSupport::TestCase def test_in_module assert A.in?(B) assert A.in?(C) - assert !A.in?(A) - assert !A.in?(D) + assert_not A.in?(A) + assert_not A.in?(D) end def test_no_method_catching diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index 903c173e59..7c7a78f461 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -37,7 +37,7 @@ class RangeTest < ActiveSupport::TestCase end def test_overlaps_last_exclusive - assert !(1...5).overlaps?(5..10) + assert_not (1...5).overlaps?(5..10) end def test_overlaps_first_inclusive @@ -45,7 +45,7 @@ class RangeTest < ActiveSupport::TestCase end def test_overlaps_first_exclusive - assert !(5..10).overlaps?(1...5) + assert_not (5..10).overlaps?(1...5) end def test_should_include_identical_inclusive @@ -102,7 +102,7 @@ class RangeTest < ActiveSupport::TestCase def test_no_overlaps_on_time time_range_1 = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30) time_range_2 = Time.utc(2005, 12, 10, 17, 31)..Time.utc(2005, 12, 10, 18, 00) - assert !time_range_1.overlaps?(time_range_2) + assert_not time_range_1.overlaps?(time_range_2) end def test_each_on_time_with_zone diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index ccaa5dc786..b8de16cc5e 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -237,11 +237,11 @@ class StringInflectionsTest < ActiveSupport::TestCase s = "hello" assert s.starts_with?("h") assert s.starts_with?("hel") - assert !s.starts_with?("el") + assert_not s.starts_with?("el") assert s.ends_with?("o") assert s.ends_with?("lo") - assert !s.ends_with?("el") + assert_not s.ends_with?("el") end def test_string_squish diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 2ca21f215e..a4fbab7b55 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -535,9 +535,9 @@ class DependenciesTest < ActiveSupport::TestCase def test_qualified_const_defined_should_not_call_const_missing ModuleWithMissing.missing_count = 0 - assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A") + assert_not ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A") assert_equal 0, ModuleWithMissing.missing_count - assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A::B") + assert_not ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A::B") assert_equal 0, ModuleWithMissing.missing_count end @@ -547,13 +547,13 @@ class DependenciesTest < ActiveSupport::TestCase def test_autoloaded? with_autoloading_fixtures do - assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") - assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder") + assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder) assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder") - assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass) @@ -564,11 +564,11 @@ class DependenciesTest < ActiveSupport::TestCase assert ActiveSupport::Dependencies.autoloaded?(:ModuleFolder) # Anonymous modules aren't autoloaded. - assert !ActiveSupport::Dependencies.autoloaded?(Module.new) + assert_not ActiveSupport::Dependencies.autoloaded?(Module.new) nil_name = Module.new def nil_name.name() nil end - assert !ActiveSupport::Dependencies.autoloaded?(nil_name) + assert_not ActiveSupport::Dependencies.autoloaded?(nil_name) end ensure remove_constants(:ModuleFolder) @@ -778,7 +778,7 @@ class DependenciesTest < ActiveSupport::TestCase M.unloadable ActiveSupport::Dependencies.clear - assert ! defined?(M) + assert_not defined?(M) Object.const_set :M, Module.new ActiveSupport::Dependencies.clear @@ -809,7 +809,7 @@ class DependenciesTest < ActiveSupport::TestCase assert_called(C, :before_remove_const, times: 1) do assert_respond_to C, :before_remove_const ActiveSupport::Dependencies.clear - assert !defined?(C) + assert_not defined?(C) end ensure remove_constants(:C) @@ -1023,7 +1023,7 @@ class DependenciesTest < ActiveSupport::TestCase assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!" end - assert !defined?(::RaisesNameError) + assert_not defined?(::RaisesNameError) 2.times do assert_raise(NameError) { ::RaisesNameError } assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!" diff --git a/activesupport/test/deprecation/proxy_wrappers_test.rb b/activesupport/test/deprecation/proxy_wrappers_test.rb index 2f866775f6..9e26052fb4 100644 --- a/activesupport/test/deprecation/proxy_wrappers_test.rb +++ b/activesupport/test/deprecation/proxy_wrappers_test.rb @@ -9,16 +9,16 @@ class ProxyWrappersTest < ActiveSupport::TestCase def test_deprecated_object_proxy_doesnt_wrap_falsy_objects proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(nil, "message") - assert !proxy + assert_not proxy end def test_deprecated_instance_variable_proxy_doesnt_wrap_falsy_objects proxy = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(nil, :waffles) - assert !proxy + assert_not proxy end def test_deprecated_constant_proxy_doesnt_wrap_falsy_objects proxy = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(Waffles, NewWaffles) - assert !proxy + assert_not proxy end end diff --git a/activesupport/test/descendants_tracker_without_autoloading_test.rb b/activesupport/test/descendants_tracker_without_autoloading_test.rb index f5c6a3045d..c65f69cba3 100644 --- a/activesupport/test/descendants_tracker_without_autoloading_test.rb +++ b/activesupport/test/descendants_tracker_without_autoloading_test.rb @@ -13,7 +13,7 @@ class DescendantsTrackerWithoutAutoloadingTest < ActiveSupport::TestCase parent_instance = Parent.new parent_instance.singleton_class.descendants ActiveSupport::DescendantsTracker.clear - assert !ActiveSupport::DescendantsTracker.class_variable_get(:@@direct_descendants).key?(parent_instance.singleton_class) + assert_not ActiveSupport::DescendantsTracker.class_variable_get(:@@direct_descendants).key?(parent_instance.singleton_class) end end end diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb index daf7f9d139..72683816b3 100644 --- a/activesupport/test/file_update_checker_shared_tests.rb +++ b/activesupport/test/file_update_checker_shared_tests.rb @@ -30,7 +30,7 @@ module FileUpdateCheckerSharedTests checker = new_checker { i += 1 } - assert !checker.execute_if_updated + assert_not checker.execute_if_updated assert_equal 0, i end @@ -41,7 +41,7 @@ module FileUpdateCheckerSharedTests checker = new_checker(tmpfiles) { i += 1 } - assert !checker.execute_if_updated + assert_not checker.execute_if_updated assert_equal 0, i end @@ -212,7 +212,7 @@ module FileUpdateCheckerSharedTests touch(tmpfile("foo.rb")) wait - assert !checker.execute_if_updated + assert_not checker.execute_if_updated assert_equal 0, i end @@ -238,7 +238,7 @@ module FileUpdateCheckerSharedTests mkdir(subdir) wait - assert !checker.execute_if_updated + assert_not checker.execute_if_updated assert_equal 0, i touch(File.join(subdir, "nested.rb")) @@ -259,7 +259,7 @@ module FileUpdateCheckerSharedTests touch(tmpfile("new.txt")) wait - assert !checker.execute_if_updated + assert_not checker.execute_if_updated assert_equal 0, i # subdir does not look for Ruby files, but its parent tmpdir does. diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb index b06250baf8..a20c428bf8 100644 --- a/activesupport/test/hash_with_indifferent_access_test.rb +++ b/activesupport/test/hash_with_indifferent_access_test.rb @@ -280,7 +280,7 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase replaced = hash.replace(b: 12) assert hash.key?("b") - assert !hash.key?(:a) + assert_not hash.key?(:a) assert_equal 12, hash[:b] assert_same hash, replaced end @@ -292,7 +292,7 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase replaced = hash.replace(HashByConversion.new(b: 12)) assert hash.key?("b") - assert !hash.key?(:a) + assert_not hash.key?(:a) assert_equal 12, hash[:b] assert_same hash, replaced end diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 05d5c1cbc3..0fa53695e0 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -25,12 +25,12 @@ class MessageVerifierTest < ActiveSupport::TestCase def test_valid_message data, hash = @verifier.generate(@data).split("--") - assert !@verifier.valid_message?(nil) - assert !@verifier.valid_message?("") - assert !@verifier.valid_message?("\xff") # invalid encoding - assert !@verifier.valid_message?("#{data.reverse}--#{hash}") - assert !@verifier.valid_message?("#{data}--#{hash.reverse}") - assert !@verifier.valid_message?("purejunk") + assert_not @verifier.valid_message?(nil) + assert_not @verifier.valid_message?("") + assert_not @verifier.valid_message?("\xff") # invalid encoding + assert_not @verifier.valid_message?("#{data.reverse}--#{hash}") + assert_not @verifier.valid_message?("#{data}--#{hash.reverse}") + assert_not @verifier.valid_message?("purejunk") end def test_simple_round_tripping @@ -40,7 +40,7 @@ class MessageVerifierTest < ActiveSupport::TestCase end def test_verified_returns_false_on_invalid_message - assert !@verifier.verified("purejunk") + assert_not @verifier.verified("purejunk") end def test_verify_exception_on_invalid_message diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 560b86b1a3..061446c782 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -75,7 +75,7 @@ class MultibyteCharsTest < ActiveSupport::TestCase def test_consumes_utf8_strings assert @proxy_class.consumes?(UNICODE_STRING) assert @proxy_class.consumes?(ASCII_STRING) - assert !@proxy_class.consumes?(BYTE_STRING) + assert_not @proxy_class.consumes?(BYTE_STRING) end def test_concatenation_should_return_a_proxy_class_instance @@ -148,7 +148,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase def test_identity assert_equal @chars, @chars assert @chars.eql?(@chars) - assert !@chars.eql?(UNICODE_STRING) + assert_not @chars.eql?(UNICODE_STRING) end def test_string_methods_are_chainable diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 28a02aafc8..d035f993f7 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -270,9 +270,9 @@ module Notifications parent.children << child assert parent.parent_of?(child) - assert !child.parent_of?(parent) - assert !parent.parent_of?(not_child) - assert !not_child.parent_of?(parent) + assert_not child.parent_of?(parent) + assert_not parent.parent_of?(not_child) + assert_not not_child.parent_of?(parent) end private diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb index cba5b8a8de..90394fee0a 100644 --- a/activesupport/test/ordered_options_test.rb +++ b/activesupport/test/ordered_options_test.rb @@ -15,7 +15,7 @@ class OrderedOptionsTest < ActiveSupport::TestCase a[:allow_concurrency] = false assert_equal 1, a.size - assert !a[:allow_concurrency] + assert_not a[:allow_concurrency] a["else_where"] = 56 assert_equal 2, a.size @@ -47,7 +47,7 @@ class OrderedOptionsTest < ActiveSupport::TestCase a.allow_concurrency = false assert_equal 1, a.size - assert !a.allow_concurrency + assert_not a.allow_concurrency a.else_where = 56 assert_equal 2, a.size diff --git a/activesupport/test/reloader_test.rb b/activesupport/test/reloader_test.rb index 3e4229eaf7..976917c1a1 100644 --- a/activesupport/test/reloader_test.rb +++ b/activesupport/test/reloader_test.rb @@ -8,18 +8,18 @@ class ReloaderTest < ActiveSupport::TestCase reloader.to_prepare { prepared = true } reloader.to_complete { completed = true } - assert !prepared - assert !completed + assert_not prepared + assert_not completed reloader.prepare! assert prepared - assert !completed + assert_not completed prepared = false reloader.wrap do assert prepared prepared = false end - assert !prepared + assert_not prepared end def test_prepend_prepare_callback @@ -42,7 +42,7 @@ class ReloaderTest < ActiveSupport::TestCase invoked = false r.to_run { invoked = true } r.wrap {} - assert !invoked + assert_not invoked end def test_full_reload_sequence diff --git a/activesupport/test/security_utils_test.rb b/activesupport/test/security_utils_test.rb index 0a607594a2..fff9cc2a8d 100644 --- a/activesupport/test/security_utils_test.rb +++ b/activesupport/test/security_utils_test.rb @@ -11,7 +11,7 @@ class SecurityUtilsTest < ActiveSupport::TestCase def test_fixed_length_secure_compare_should_perform_string_comparison assert ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "a") - assert !ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "b") + assert_not ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "b") end def test_fixed_length_secure_compare_raise_on_length_mismatch diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 63ca22efb5..b59f3e9405 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -725,6 +725,21 @@ class TimeZoneTest < ActiveSupport::TestCase assert_not_includes all_zones, galapagos end + def test_all_doesnt_raise_exception_with_missing_tzinfo_data + mappings = { + "Puerto Rico" => "America/Unknown", + "Pittsburgh" => "America/New_York" + } + + with_tz_mappings(mappings) do + assert_nil ActiveSupport::TimeZone["Puerto Rico"] + assert_nil ActiveSupport::TimeZone[-9] + assert_nothing_raised do + ActiveSupport::TimeZone.all + end + end + end + def test_index assert_nil ActiveSupport::TimeZone["bogus"] assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone["Central Time (US & Canada)"] diff --git a/activesupport/test/time_zone_test_helpers.rb b/activesupport/test/time_zone_test_helpers.rb index 051703a781..85ed727c9b 100644 --- a/activesupport/test/time_zone_test_helpers.rb +++ b/activesupport/test/time_zone_test_helpers.rb @@ -23,4 +23,17 @@ module TimeZoneTestHelpers ensure ActiveSupport.to_time_preserves_timezone = old_preserve_tz end + + def with_tz_mappings(mappings) + old_mappings = ActiveSupport::TimeZone::MAPPING.dup + ActiveSupport::TimeZone.clear + ActiveSupport::TimeZone::MAPPING.clear + ActiveSupport::TimeZone::MAPPING.merge!(mappings) + + yield + ensure + ActiveSupport::TimeZone.clear + ActiveSupport::TimeZone::MAPPING.clear + ActiveSupport::TimeZone::MAPPING.merge!(old_mappings) + end end diff --git a/ci/custom_cops/lib/custom_cops.rb b/ci/custom_cops/lib/custom_cops.rb index d5d17f8856..157b8247e4 100644 --- a/ci/custom_cops/lib/custom_cops.rb +++ b/ci/custom_cops/lib/custom_cops.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true require_relative "custom_cops/refute_not" +require_relative "custom_cops/assert_not" diff --git a/ci/custom_cops/lib/custom_cops/assert_not.rb b/ci/custom_cops/lib/custom_cops/assert_not.rb new file mode 100644 index 0000000000..e722448e21 --- /dev/null +++ b/ci/custom_cops/lib/custom_cops/assert_not.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module CustomCops + # Enforces the use of `assert_not` over `assert !`. + # + # @example + # # bad + # assert !x + # assert ! x + # + # # good + # assert_not x + # + class AssertNot < RuboCop::Cop::Cop + MSG = "Prefer `assert_not` over `assert !`" + + def_node_matcher :offensive?, "(send nil? :assert (send ... :!))" + + def on_send(node) + add_offense(node) if offensive?(node) + end + + def autocorrect(node) + expression = node.loc.expression + + ->(corrector) do + corrector.replace( + expression, + corrected_source(expression.source) + ) + end + end + + private + + def corrected_source(source) + source.gsub(/^assert(\(| ) *! */, "assert_not\\1") + end + end +end diff --git a/ci/custom_cops/test/custom_cops/assert_not_test.rb b/ci/custom_cops/test/custom_cops/assert_not_test.rb new file mode 100644 index 0000000000..abb151aeb4 --- /dev/null +++ b/ci/custom_cops/test/custom_cops/assert_not_test.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "support/cop_helper" +require_relative "../../lib/custom_cops/assert_not" + +class AssertNotTest < ActiveSupport::TestCase + include CopHelper + + setup do + @cop = CustomCops::AssertNot.new + end + + test "rejects 'assert !'" do + inspect_source @cop, "assert !x" + assert_offense @cop, "^^^^^^^^^ Prefer `assert_not` over `assert !`" + end + + test "rejects 'assert !' with a complex value" do + inspect_source @cop, "assert !a.b(c)" + assert_offense @cop, "^^^^^^^^^^^^^^ Prefer `assert_not` over `assert !`" + end + + test "autocorrects `assert !`" do + corrected = autocorrect_source(@cop, "assert !false") + assert_equal "assert_not false", corrected + end + + test "autocorrects `assert !` with extra spaces" do + corrected = autocorrect_source(@cop, "assert ! false") + assert_equal "assert_not false", corrected + end + + test "autocorrects `assert !` with parentheses" do + corrected = autocorrect_source(@cop, "assert(!false)") + assert_equal "assert_not(false)", corrected + end + + test "accepts `assert_not`" do + inspect_source @cop, "assert_not x" + assert_empty @cop.offenses + end +end diff --git a/ci/custom_cops/test/custom_cops/refute_not_test.rb b/ci/custom_cops/test/custom_cops/refute_not_test.rb index 5dbd8bf32a..f0f6eaeda0 100644 --- a/ci/custom_cops/test/custom_cops/refute_not_test.rb +++ b/ci/custom_cops/test/custom_cops/refute_not_test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "support/cop_helper" -require "./lib/custom_cops/refute_not" +require_relative "../../lib/custom_cops/refute_not" class RefuteNotTest < ActiveSupport::TestCase include CopHelper @@ -59,15 +59,6 @@ class RefuteNotTest < ActiveSupport::TestCase private - def assert_offense(cop, expected_message) - assert_not_empty cop.offenses - - offense = cop.offenses.first - carets = "^" * offense.column_length - - assert_equal expected_message, "#{carets} #{offense.message}" - end - def offense_message(refute_method, assert_method) carets = "^" * refute_method.to_s.length "#{carets} Prefer `#{assert_method}` over `#{refute_method}`" diff --git a/ci/custom_cops/test/support/cop_helper.rb b/ci/custom_cops/test/support/cop_helper.rb index d259154df5..c2c6b969dd 100644 --- a/ci/custom_cops/test/support/cop_helper.rb +++ b/ci/custom_cops/test/support/cop_helper.rb @@ -16,6 +16,18 @@ module CopHelper rewrite(cop, processed_source) end + def assert_offense(cop, expected_message) + assert_not_empty( + cop.offenses, + "Expected offense with message \"#{expected_message}\", but got no offense" + ) + + offense = cop.offenses.first + carets = "^" * offense.column_length + + assert_equal expected_message, "#{carets} #{offense.message}" + end + private TARGET_RUBY_VERSION = 2.4 diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css index b27776745a..a3ab745234 100644 --- a/guides/assets/stylesheets/main.css +++ b/guides/assets/stylesheets/main.css @@ -70,7 +70,7 @@ table { } table th, table td { - padding: 0.25em 1em; + padding: 9px 10px; border: 1px solid #CCC; border-collapse: collapse; } @@ -79,7 +79,6 @@ table th { border-bottom: 2px solid #CCC; background: #EEE; font-weight: bold; - padding: 0.5em 1em; } img { @@ -265,8 +264,6 @@ body { } } -#extraCol {display: none;} - #footer { padding: 2em 0; background: #222 url(../images/footer_tile.gif) repeat-x; @@ -555,8 +552,6 @@ h6 { font-size: 1.2857em; padding: 0.125em 0 0.25em 0; margin-bottom: 0; - /*background: url(../images/book_icon.gif) no-repeat left top; - padding: 0.125em 0 0.25em 28px;*/ } @media screen and (max-width: 480px) { @@ -665,10 +660,8 @@ div.code_container { visibility: hidden; } -.clearfix {display: inline-block;} * html .clearfix {height: 1%;} .clearfix {display: block;} -.clear { clear:both; } /* Same bottom margin for special boxes than for regular paragraphs, this way intermediate whitespace looks uniform. */ @@ -696,9 +689,6 @@ div.important p, div.caution p, div.warning p, div.note p, div.info p { /* Foundation v2.1.4 http://foundation.zurb.com */ /* Artfully masterminded by ZURB */ -table th { font-weight: bold; } -table td, table th { padding: 9px 10px; text-align: left; } - /* Mobile */ @media only screen and (max-width: 767px) { table.responsive { margin-bottom: 0; } diff --git a/guides/assets/stylesheets/print.css b/guides/assets/stylesheets/print.css index bdc8ec948d..6280422469 100644 --- a/guides/assets/stylesheets/print.css +++ b/guides/assets/stylesheets/print.css @@ -4,7 +4,7 @@ /* Modified January 31, 2009 --------------------------------------- */ -body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, #extraCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;} +body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;} body { background: #FFF; diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index cd33e2119a..5dd6bfdd23 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -10,12 +10,13 @@ </p> <% else %> <p> - These are the new guides for Rails 5.1 based on <a href="https://github.com/rails/rails/tree/<%= @version %>"><%= @version %></a>. + These are the new guides for Rails 5.2 based on <a href="https://github.com/rails/rails/tree/<%= @version %>"><%= @version %></a>. These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together. </p> <% end %> <p> The guides for earlier releases: +<a href="http://guides.rubyonrails.org/v5.2/">Rails 5.2</a>, <a href="http://guides.rubyonrails.org/v5.1/">Rails 5.1</a>, <a href="http://guides.rubyonrails.org/v5.0/">Rails 5.0</a>, <a href="http://guides.rubyonrails.org/v4.2/">Rails 4.2</a>, diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index c01d1082b6..37b8843d1e 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -1267,8 +1267,8 @@ password_field_tag 'pass' Creates a radio button; use groups of radio buttons named the same to allow users to select from a group of options. ```ruby -radio_button_tag 'gender', 'male' -# => <input id="gender_male" name="gender" type="radio" value="male" /> +radio_button_tag 'favorite_color', 'maroon' +# => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" /> ``` #### select_tag diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 2f5854fed0..88b87b78d2 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -728,8 +728,8 @@ Rails.application.config.assets.precompile += %w( admin.js admin.css ) NOTE. Always specify an expected compiled filename that ends with `.js` or `.css`, even if you want to add Sass or CoffeeScript files to the precompile array. -The task also generates a `.sprockets-manifest-md5hash.json` (where `md5hash` is -an MD5 hash) that contains a list with all your assets and their respective +The task also generates a `.sprockets-manifest-randomhex.json` (where `randomhex` is +a 16-byte random hex string) that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods to avoid handing the mapping requests back to Sprockets. A typical manifest file looks like: diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 339b356a78..f42ab15b8b 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -829,14 +829,14 @@ For example when you add the following translations: en: activerecord: models: - user: Dude + user: Customer attributes: user: login: "Handle" # will translate User attribute "login" as "Handle" ``` -Then `User.model_name.human` will return "Dude" and `User.human_attribute_name("login")` will return "Handle". +Then `User.model_name.human` will return "Customer" and `User.human_attribute_name("login")` will return "Handle". You can also set a plural form for model names, adding as following: @@ -845,11 +845,11 @@ en: activerecord: models: user: - one: Dude - other: Dudes + one: Customer + other: Customers ``` -Then `User.model_name.human(count: 2)` will return "Dudes". With `count: 1` or without params will return "Dude". +Then `User.model_name.human(count: 2)` will return "Customers". With `count: 1` or without params will return "Customer". In the event you need to access nested attributes within a given model, you should nest these under `model/attribute` at the model level of your translation file: @@ -857,12 +857,12 @@ In the event you need to access nested attributes within a given model, you shou en: activerecord: attributes: - user/gender: - female: "Female" - male: "Male" + user/role: + admin: "Admin" + contributor: "Contributor" ``` -Then `User.human_attribute_name("gender.female")` will return "Female". +Then `User.human_attribute_name("role.admin")` will return "Admin". NOTE: If you are using a class which includes `ActiveModel` and does not inherit from `ActiveRecord::Base`, replace `activerecord` with `activemodel` in the above key paths. diff --git a/guides/source/security.md b/guides/source/security.md index a21526d895..3ac50fb147 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1182,6 +1182,12 @@ as part of `html_options`. Example: <% end -%> ``` +The same works with `javascript_include_tag`: + +```html+erb +<%= javascript_include_tag "script", nonce: true %> +``` + Use [`csp_meta_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/CspHelper.html#method-i-csp_meta_tag) helper to create a meta tag "csp-nonce" with the per-session nonce value for allowing inline `<script>` tags. diff --git a/guides/source/testing.md b/guides/source/testing.md index b9b310cbba..82bf38c206 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -690,7 +690,7 @@ System Testing -------------- System tests allow you to test user interactions with your application, running tests -in either a real or a headless browser. System tests uses Capybara under the hood. +in either a real or a headless browser. System tests use Capybara under the hood. For creating Rails system tests, you use the `test/system` directory in your application. Rails provides a generator to create a system test skeleton for you. @@ -1484,7 +1484,7 @@ located under the `test/helpers` directory. Given we have the following helper: ```ruby -module UserHelper +module UsersHelper def link_to_user(user) link_to "#{user.first_name} #{user.last_name}", user end @@ -1494,7 +1494,7 @@ end We can test the output of this method like this: ```ruby -class UserHelperTest < ActionView::TestCase +class UsersHelperTest < ActionView::TestCase test "should return the user's full name" do user = users(:david) @@ -1600,7 +1600,7 @@ Functional testing for mailers involves more than just checking that the email b ```ruby require 'test_helper' -class UserControllerTest < ActionDispatch::IntegrationTest +class UsersControllerTest < ActionDispatch::IntegrationTest test "invite friend" do assert_difference 'ActionMailer::Base.deliveries.size', +1 do post invite_friend_url, params: { email: 'friend@example.com' } diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index b9ea4ad47a..a922bdc16b 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -382,10 +382,9 @@ have been bundled into `event.detail`. For information about the previously used `jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax). ### Stoppable events - -If you stop `ajax:before` or `ajax:beforeSend` by returning false from the -handler method, the Ajax request will never take place. The `ajax:before` event -can manipulate form data before serialization and the +You can stop execution of the Ajax request by running `event.preventDefault()` +from the handlers methods `ajax:before` or `ajax:beforeSend`. +The `ajax:before` event can manipulate form data before serialization and the `ajax:beforeSend` event is useful for adding custom request headers. If you stop the `ajax:aborted:file` event, the default behavior of allowing the @@ -393,6 +392,9 @@ browser to submit the form via normal means (i.e. non-Ajax submission) will be canceled and the form will not be submitted at all. This is useful for implementing your own Ajax file upload workaround. +Note, you should use `return false` to prevent event for `jquery-ujs` and +`e.preventDefault()` for `rails-ujs` + Server-Side Concerns -------------------- diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 3b4b5330f7..a4d4a87a8b 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,28 @@ +* Make the master.key file read-only for the owner upon generation on + POSIX-compliant systems. + + Previously: + + $ ls -l config/master.key + -rw-r--r-- 1 owner group 32 Jan 1 00:00 master.key + + Now: + + $ ls -l config/master.key + -rw------- 1 owner group 32 Jan 1 00:00 master.key + + Fixes #32604. + + *Jose Luis Duran* + +* Deprecate support for using the `HOST` environment to specify the server IP. + + The `BINDING` environment should be used instead. + + Fixes #29516. + + *Yuji Yaginuma* + * Deprecate passing Rack server name as a regular argument to `rails server`. Previously: diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc index 5b4f23521e..a4a4b6b235 100644 --- a/railties/RDOC_MAIN.rdoc +++ b/railties/RDOC_MAIN.rdoc @@ -1,4 +1,6 @@ -== Welcome to \Rails += Welcome to \Rails + +== What's \Rails \Rails is a web-application framework that includes everything needed to create database-backed web applications according to the @@ -8,41 +10,46 @@ pattern. Understanding the MVC pattern is key to understanding \Rails. MVC divides your application into three layers: Model, View, and Controller, each with a specific responsibility. -The <em>Model layer</em> represents the domain model (such as Account, Product, -Person, Post, etc.) and encapsulates the business logic that is specific to +== Model layer + +The <em><b>Model layer</b></em> represents the domain model (such as Account, Product, +Person, Post, etc.) and encapsulates the business logic specific to your application. In \Rails, database-backed model classes are derived from -ActiveRecord::Base. Active Record allows you to present the data from +<tt>ActiveRecord::Base</tt>. {Active Record}[link:files/activerecord/README_rdoc.html] allows you to present the data from database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in its {README}[link:files/activerecord/README_rdoc.html]. -Although most \Rails models are backed by a database, models can also be ordinary +methods. Although most \Rails models are backed by a database, models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by -the Active Model module. You can read more about Active Model in its {README}[link:files/activemodel/README_rdoc.html]. +the {Active Model}[link:files/activemodel/README_rdoc.html] module. + +== Controller layer -The <em>Controller layer</em> is responsible for handling incoming HTTP requests and +The <em><b>Controller layer</b></em> is responsible for handling incoming HTTP requests and providing a suitable response. Usually this means returning \HTML, but \Rails controllers can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers load and manipulate models, and render view templates in order to generate the appropriate HTTP response. In \Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and -controller classes are derived from ActionController::Base. Action Dispatch and Action Controller -are bundled together in Action Pack. You can read more about Action Pack in its -{README}[link:files/actionpack/README_rdoc.html]. +controller classes are derived from <tt>ActionController::Base</tt>. Action Dispatch and Action Controller +are bundled together in {Action Pack}[link:files/actionpack/README_rdoc.html]. -The <em>View layer</em> is composed of "templates" that are responsible for providing +== View layer + +The <em><b>View layer</b></em> is composed of "templates" that are responsible for providing appropriate representations of your application's resources. Templates can come in a variety of formats, but most view templates are \HTML with embedded Ruby code (ERB files). Views are typically rendered to generate a controller response, -or to generate the body of an email. In \Rails, View generation is handled by Action View. -You can read more about Action View in its {README}[link:files/actionview/README_rdoc.html]. +or to generate the body of an email. In \Rails, View generation is handled by {Action View}[link:files/actionview/README_rdoc.html]. + +== Frameworks and libraries -Active Record, Active Model, Action Pack, and Action View can each be used independently outside \Rails. -In addition to that, \Rails also comes with Action Mailer ({README}[link:files/actionmailer/README_rdoc.html]), a library -to generate and send emails; Active Job ({README}[link:files/activejob/README_md.html]), a +{Active Record}[link:files/activerecord/README_rdoc.html], {Active Model}[link:files/activemodel/README_rdoc.html], +{Action Pack}[link:files/actionpack/README_rdoc.html], and {Action View}[link:files/actionview/README_rdoc.html] can each be used independently outside \Rails. +In addition to that, \Rails also comes with {Action Mailer}[link:files/actionmailer/README_rdoc.html], a library +to generate and send emails; {Active Job}[link:files/activejob/README_md.html], a framework for declaring jobs and making them run on a variety of queueing -backends; Action Cable ({README}[link:files/actioncable/README_md.html]), a framework to -integrate WebSockets with a \Rails application; -Active Storage ({README}[link:files/activestorage/README_md.html]), a library to attach cloud -and local files to \Rails applications; -and Active Support ({README}[link:files/activesupport/README_rdoc.html]), a collection +backends; {Action Cable}[link:files/actioncable/README_md.html], a framework to +integrate WebSockets with a \Rails application; {Active Storage}[link:files/activestorage/README_md.html], +a library to attach cloud and local files to \Rails applications; +and {Active Support}[link:files/activesupport/README_rdoc.html], a collection of utility classes and standard library extensions that are useful for \Rails, and may also be used independently outside \Rails. diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index 6da300e356..77b6c1f65d 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -219,7 +219,7 @@ module Rails user_supplied_options << name end end - user_supplied_options << :Host if ENV["HOST"] + user_supplied_options << :Host if ENV["HOST"] || ENV["BINDING"] user_supplied_options << :Port if ENV["PORT"] user_supplied_options.uniq end @@ -234,7 +234,17 @@ module Rails options[:binding] else default_host = environment == "development" ? "localhost" : "0.0.0.0" - ENV.fetch("HOST", default_host) + + if ENV["HOST"] && !ENV["BINDING"] + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Using the `HOST` environment to specify the IP is deprecated and will be removed in Rails 6.1. + Please use `BINDING` environment instead. + MSG + + return ENV["HOST"] + end + + ENV.fetch("BINDING", default_host) end end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 8c5d872573..f51542f3ec 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -440,7 +440,7 @@ module Rails end def depend_on_bootsnap? - !options[:skip_bootsnap] && !options[:dev] + !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION) end def os_supports_listen_out_of_the_box? diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt index c444f33b0f..19f0d7f202 100644 --- a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt +++ b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt @@ -1 +1 @@ -<%= RUBY_VERSION -%> +<%= "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" -%> diff --git a/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb index 90068c678d..e2359e9ded 100644 --- a/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +++ b/railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb @@ -27,6 +27,7 @@ module Rails def add_key_file_silently(key_path, key = nil) create_file key_path, key || ActiveSupport::EncryptedFile.generate_key + key_path.chmod 0600 end def ignore_key_file(key_path, ignore: key_ignore(key_path)) diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb index 73bee5bcbf..7257aaeaae 100644 --- a/railties/lib/rails/source_annotation_extractor.rb +++ b/railties/lib/rails/source_annotation_extractor.rb @@ -29,7 +29,7 @@ module Rails end # Registers additional directories to be included - # SourceAnnotationExtractor::Annotation.register_directories("spec", "another") + # Rails::SourceAnnotationExtractor::Annotation.register_directories("spec", "another") def self.register_directories(*dirs) directories.push(*dirs) end @@ -39,7 +39,7 @@ module Rails end # Registers new Annotations File Extensions - # SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ } + # Rails::SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ } def self.register_extensions(*exts, &block) extensions[/\.(#{exts.join("|")})$/] = block end @@ -69,7 +69,7 @@ module Rails # # Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+. # - # SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true + # Rails::SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true # # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+. # @@ -95,9 +95,9 @@ module Rails end # Returns a hash that maps filenames under +dir+ (recursively) to arrays - # with their annotations. Only files with annotations are included. Files - # with extension +.builder+, +.rb+, +.rake+, +.yml+, +.yaml+, +.ruby+, - # +.css+, +.js+ and +.erb+ are taken into account. + # with their annotations. Files with extensions registered in + # <tt>Rails::SourceAnnotationExtractor::Annotation.extensions</tt> are + # taken into account. Only files with annotations are included. def find_in(dir) results = {} diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb index bb556f1968..c7a6bdee1b 100644 --- a/railties/test/app_loader_test.rb +++ b/railties/test/app_loader_test.rb @@ -40,13 +40,13 @@ class AppLoaderTest < ActiveSupport::TestCase test "is not in a Rails application if #{exe} is not found in the current or parent directories" do def loader.find_executables; end - assert !loader.exec_app + assert_not loader.exec_app end test "is not in a Rails application if #{exe} exists but is a folder" do FileUtils.mkdir_p(exe) - assert !loader.exec_app + assert_not loader.exec_app end ["APP_PATH", "ENGINE_PATH"].each do |keyword| @@ -61,7 +61,7 @@ class AppLoaderTest < ActiveSupport::TestCase test "is not in a Rails application if #{exe} exists but doesn't contain #{keyword}" do write exe - assert !loader.exec_app + assert_not loader.exec_app end test "is in a Rails application if parent directory has #{exe} containing #{keyword} and chdirs to the root directory" do diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index 0d3262d6f6..9ef123c5b6 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -76,7 +76,7 @@ module ApplicationTests # Load app env app "production" - assert !defined?(Uglifier) + assert_not defined?(Uglifier) get "/assets/demo.js" assert_match "alert()", last_response.body assert defined?(Uglifier) @@ -270,10 +270,10 @@ module ApplicationTests app "production" # Checking if Uglifier is defined we can know if Sprockets was reached or not - assert !defined?(Uglifier) + assert_not defined?(Uglifier) get "/assets/#{asset_path}" assert_match "alert()", last_response.body - assert !defined?(Uglifier) + assert_not defined?(Uglifier) end test "precompile properly refers files referenced with asset_path" do diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 420abe8673..84606d3b90 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -361,7 +361,7 @@ module ApplicationTests end RUBY - assert !$prepared + assert_not $prepared app "development" @@ -1417,7 +1417,7 @@ module ApplicationTests assert_equal "XML", last_response.body end - test "Rails.application#env_config exists and include some existing parameters" do + test "Rails.application#env_config exists and includes some existing parameters" do make_basic_app assert_equal app.env_config["action_dispatch.parameter_filter"], app.config.filter_parameters diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index e631318f82..1530ea82d6 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -226,7 +226,7 @@ module ApplicationTests rails %w(generate model post title:string) rails %w(db:migrate db:schema:cache:dump db:rollback) require "#{app_path}/config/environment" - assert !ActiveRecord::Base.connection.schema_cache.data_sources("posts") + assert_not ActiveRecord::Base.connection.schema_cache.data_sources("posts") end test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb index 9def3a0ce7..818ad61c64 100644 --- a/railties/test/application/middleware/sendfile_test.rb +++ b/railties/test/application/middleware/sendfile_test.rb @@ -29,7 +29,7 @@ module ApplicationTests simple_controller get "/" - assert !last_response.headers["X-Sendfile"] + assert_not last_response.headers["X-Sendfile"] assert_equal File.read(__FILE__), last_response.body end diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index a17988235a..9182a63ab7 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -31,7 +31,7 @@ module ApplicationTests add_to_config "config.force_ssl = true" add_to_config "config.ssl_options = { secure_cookies: false }" require "#{app_path}/config/environment" - assert !app.config.session_options[:secure] + assert_not app.config.session_options[:secure] end test "session is not loaded if it's not used" do @@ -51,7 +51,7 @@ module ApplicationTests get "/" assert last_request.env["HTTP_COOKIE"] - assert !last_response.headers["Set-Cookie"] + assert_not last_response.headers["Set-Cookie"] end test "session is empty and isn't saved on unverified request when using :null_session protect method" do diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 5b4c42c189..0594236b1f 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -34,7 +34,7 @@ module ApplicationTests assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded output = rails("db:drop") assert_match(/Dropped database/, output) - assert !File.exist?(expected_database) + assert_not File.exist?(expected_database) end end diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb index 43a433d40f..07d96fcb56 100644 --- a/railties/test/application/rake/multi_dbs_test.rb +++ b/railties/test/application/rake/multi_dbs_test.rb @@ -26,7 +26,7 @@ module ApplicationTests output = rails("db:drop") assert_match(/Dropped database/, output) assert_match_namespace(namespace, output) - assert !File.exist?(expected_database) + assert_not File.exist?(expected_database) end end @@ -40,7 +40,7 @@ module ApplicationTests output = rails("db:drop:#{namespace}") assert_match(/Dropped database/, output) assert_match_namespace(namespace, output) - assert !File.exist?(expected_database) + assert_not File.exist?(expected_database) end end @@ -67,8 +67,8 @@ module ApplicationTests else schema_dump = File.read("db/#{format}.sql") schema_dump_animals = File.read("db/animals_#{format}.sql") - assert_match(/CREATE TABLE \"books\"/, schema_dump) - assert_match(/CREATE TABLE \"dogs\"/, schema_dump_animals) + assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"books\"/, schema_dump) + assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"dogs\"/, schema_dump_animals) end rails "db:#{format}:load" diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index d94caf5fe0..1522a2bbc5 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -229,7 +229,7 @@ module ApplicationTests def test_rake_clear_schema_cache rails "db:schema:cache:dump", "db:schema:cache:clear" - assert !File.exist?(File.join(app_path, "db", "schema_cache.yml")) + assert_not File.exist?(File.join(app_path, "db", "schema_cache.yml")) end def test_copy_templates diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb index 6ad96b28c7..0aea21051a 100644 --- a/railties/test/commands/dbconsole_test.rb +++ b/railties/test/commands/dbconsole_test.rb @@ -123,31 +123,31 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_mysql start(adapter: "mysql2", database: "db") - assert !aborted + assert_not aborted assert_equal [%w[mysql mysql5], "db"], dbconsole.find_cmd_and_exec_args end def test_mysql_full start(adapter: "mysql2", database: "db", host: "locahost", port: 1234, socket: "socket", username: "user", password: "qwerty", encoding: "UTF-8") - assert !aborted + assert_not aborted assert_equal [%w[mysql mysql5], "--host=locahost", "--port=1234", "--socket=socket", "--user=user", "--default-character-set=UTF-8", "-p", "db"], dbconsole.find_cmd_and_exec_args end def test_mysql_include_password start({ adapter: "mysql2", database: "db", username: "user", password: "qwerty" }, ["-p"]) - assert !aborted + assert_not aborted assert_equal [%w[mysql mysql5], "--user=user", "--password=qwerty", "db"], dbconsole.find_cmd_and_exec_args end def test_postgresql start(adapter: "postgresql", database: "db") - assert !aborted + assert_not aborted assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args end def test_postgresql_full start(adapter: "postgresql", database: "db", username: "user", password: "q1w2e3", host: "host", port: 5432) - assert !aborted + assert_not aborted assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args assert_equal "user", ENV["PGUSER"] assert_equal "host", ENV["PGHOST"] @@ -157,7 +157,7 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_postgresql_include_password start({ adapter: "postgresql", database: "db", username: "user", password: "q1w2e3" }, ["-p"]) - assert !aborted + assert_not aborted assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args assert_equal "user", ENV["PGUSER"] assert_equal "q1w2e3", ENV["PGPASSWORD"] @@ -165,13 +165,13 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_sqlite3 start(adapter: "sqlite3", database: "db.sqlite3") - assert !aborted + assert_not aborted assert_equal ["sqlite3", Rails.root.join("db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args end def test_sqlite3_mode start({ adapter: "sqlite3", database: "db.sqlite3" }, ["--mode", "html"]) - assert !aborted + assert_not aborted assert_equal ["sqlite3", "-html", Rails.root.join("db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args end @@ -182,27 +182,27 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase def test_sqlite3_db_absolute_path start(adapter: "sqlite3", database: "/tmp/db.sqlite3") - assert !aborted + assert_not aborted assert_equal ["sqlite3", "/tmp/db.sqlite3"], dbconsole.find_cmd_and_exec_args end def test_sqlite3_db_without_defined_rails_root Rails.stub(:respond_to?, false) do start(adapter: "sqlite3", database: "config/db.sqlite3") - assert !aborted + assert_not aborted assert_equal ["sqlite3", Rails.root.join("../config/db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args end end def test_oracle start(adapter: "oracle", database: "db", username: "user", password: "secret") - assert !aborted + assert_not aborted assert_equal ["sqlplus", "user@db"], dbconsole.find_cmd_and_exec_args end def test_oracle_include_password start({ adapter: "oracle", database: "db", username: "user", password: "secret" }, ["-p"]) - assert !aborted + assert_not aborted assert_equal ["sqlplus", "user/secret@db"], dbconsole.find_cmd_and_exec_args end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index bdaab477f1..e7a56b3e6d 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -90,6 +90,15 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase def test_environment_with_host switch_env "HOST", "1.2.3.4" do + assert_deprecated do + options = parse_arguments + assert_equal "1.2.3.4", options[:Host] + end + end + end + + def test_environment_with_binding + switch_env "BINDING", "1.2.3.4" do options = parse_arguments assert_equal "1.2.3.4", options[:Host] end @@ -196,7 +205,7 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase assert_equal 3000, options[:Port] end - switch_env "HOST", "1.2.3.4" do + switch_env "BINDING", "1.2.3.4" do args = ["-b", "127.0.0.1"] options = parse_arguments(args) assert_equal "127.0.0.1", options[:Host] @@ -215,6 +224,11 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase server_options = parse_arguments(["--port=3001"]) assert_equal [:Port], server_options[:user_supplied_options] + + switch_env "BINDING", "1.2.3.4" do + server_options = parse_arguments + assert_equal [:Host], server_options[:user_supplied_options] + end end def test_default_options diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 294fdcd6a1..cd607ca8d8 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -816,9 +816,18 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_bootsnap run_generator - assert_gem "bootsnap" - assert_file "config/boot.rb" do |content| - assert_match(/require 'bootsnap\/setup'/, content) + unless defined?(JRUBY_VERSION) + assert_gem "bootsnap" + assert_file "config/boot.rb" do |content| + assert_match(/require 'bootsnap\/setup'/, content) + end + else + assert_file "Gemfile" do |content| + assert_no_match(/bootsnap/, content) + end + assert_file "config/boot.rb" do |content| + assert_no_match(/require 'bootsnap\/setup'/, content) + end end end @@ -851,7 +860,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_match(/ruby '#{RUBY_VERSION}'/, content) end assert_file ".ruby-version" do |content| - assert_match(/#{RUBY_VERSION}/, content) + assert_match(/#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}/, content) end end @@ -941,6 +950,15 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_directory("test/system") end + unless Gem.win_platform? + def test_master_key_is_only_readable_by_the_owner + run_generator + + stat = File.stat("config/master.key") + assert_equal "100600", sprintf("%o", stat.mode) + end + end + private def stub_rails_application(root) Rails.application.config.root = root diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb index 43b60b9144..50522c1be6 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -9,7 +9,7 @@ class InfoTest < ActiveSupport::TestCase property("Bogus") { raise } end end - assert !property_defined?("Bogus") + assert_not property_defined?("Bogus") end def test_property_with_string diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index a59c63f343..9a3ddc8d5e 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -226,7 +226,7 @@ module RailtiesTest require "rdoc/task" require "rake/testtask" Rails.application.load_tasks - assert !Rake::Task.task_defined?("bukkits:install:migrations") + assert_not Rake::Task.task_defined?("bukkits:install:migrations") end test "puts its lib directory on load path" do @@ -745,7 +745,7 @@ YAML assert_equal "bukkits", Bukkits::Engine.engine_name assert_equal Bukkits.railtie_namespace, Bukkits::Engine assert ::Bukkits::MyMailer.method_defined?(:foo_url) - assert !::Bukkits::MyMailer.method_defined?(:bar_url) + assert_not ::Bukkits::MyMailer.method_defined?(:bar_url) get("/bukkits/from_app") assert_equal "false", last_response.body diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb index 359ab0fdae..7c3d1e3759 100644 --- a/railties/test/railties/railtie_test.rb +++ b/railties/test/railties/railtie_test.rb @@ -65,7 +65,7 @@ module RailtiesTest test "railtie can add to_prepare callbacks" do $to_prepare = false class Foo < Rails::Railtie ; config.to_prepare { $to_prepare = true } ; end - assert !$to_prepare + assert_not $to_prepare require "#{app_path}/config/environment" require "rack/test" extend Rack::Test::Methods @@ -91,7 +91,7 @@ module RailtiesTest test "railtie can add after_initialize callbacks" do $after_initialize = false class Foo < Rails::Railtie ; config.after_initialize { $after_initialize = true } ; end - assert !$after_initialize + assert_not $after_initialize require "#{app_path}/config/environment" assert $after_initialize end @@ -107,7 +107,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block require "rake" require "rake/testtask" require "rdoc/task" @@ -151,7 +151,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block Rails.application.load_generators assert $ran_block end @@ -167,7 +167,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block Rails.application.load_console assert $ran_block end @@ -183,7 +183,7 @@ module RailtiesTest require "#{app_path}/config/environment" - assert !$ran_block + assert_not $ran_block Rails.application.load_runner assert $ran_block end @@ -197,7 +197,7 @@ module RailtiesTest end end - assert !$ran_block + assert_not $ran_block require "#{app_path}/config/environment" assert $ran_block end |