diff options
234 files changed, 959 insertions, 569 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index 8663034a1a..7dfb7b4b81 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -93,6 +93,10 @@ Style/FrozenStringLiteralComment: - 'activejob/**/*' - 'activerecord/**/*' - 'actionmailer/**/*' + - 'actionview/**/*' + Exclude: + - 'actionview/test/**/*.builder' + - 'actionview/test/**/*.ruby' # Use `foo {}` not `foo{}`. Layout/SpaceBeforeBlockBraces: @@ -22,7 +22,7 @@ gem "capybara", "~> 2.13" gem "rack-cache", "~> 1.2" gem "jquery-rails" gem "coffee-rails" -gem "sass-rails" +gem "sass-rails", github: "rails/sass-rails", branch: "5-0-stable" gem "turbolinks", "~> 5" # require: false so bcrypt is loaded only when has_secure_password is used. diff --git a/Gemfile.lock b/Gemfile.lock index f8b7e7ce9f..304e44112d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -31,10 +31,22 @@ GIT GIT remote: https://github.com/rails/arel.git - revision: 67a51c62f4e19390cd8eb408596ca48bb0806362 + revision: 7a29220c689feb0581e21d5324b85fc2f201ac5e specs: arel (8.0.0) +GIT + remote: https://github.com/rails/sass-rails.git + revision: bb5c1d34e8acad2e2960cc785184ffe17d7b3bca + branch: 5-0-stable + specs: + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + PATH remote: . specs: @@ -313,12 +325,6 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sass-rails (5.0.6) - railties (>= 4.0.0, < 6) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) sdoc (1.0.0.rc2) rdoc (~> 5.0) selenium-webdriver (3.4.4) @@ -364,7 +370,7 @@ GEM rack (>= 1, < 3) thread (0.1.7) thread_safe (0.3.6) - tilt (2.0.7) + tilt (2.0.8) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.3) @@ -439,7 +445,7 @@ DEPENDENCIES resque resque-scheduler rubocop (>= 0.47) - sass-rails + sass-rails! sdoc (> 1.0.0.rc1, < 2.0) sequel sidekiq @@ -457,4 +463,4 @@ DEPENDENCIES websocket-client-simple! BUNDLED WITH - 1.15.2 + 1.15.3 diff --git a/actionview/Rakefile b/actionview/Rakefile index 2328e82d05..0d974cb087 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "rake/testtask" require "fileutils" require "open3" diff --git a/actionview/actionview.gemspec b/actionview/actionview.gemspec index 48e79c53ca..b99137fcf6 100644 --- a/actionview/actionview.gemspec +++ b/actionview/actionview.gemspec @@ -1,3 +1,5 @@ +# frozen_string_literal: true + version = File.read(File.expand_path("../RAILS_VERSION", __dir__)).strip Gem::Specification.new do |s| diff --git a/actionview/bin/test b/actionview/bin/test index 470ce93f10..c53377cc97 100755 --- a/actionview/bin/test +++ b/actionview/bin/test @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true COMPONENT_ROOT = File.expand_path("..", __dir__) require_relative "../../tools/test" diff --git a/actionview/lib/action_view.rb b/actionview/lib/action_view.rb index 53a83d48f6..3c8a8488a5 100644 --- a/actionview/lib/action_view.rb +++ b/actionview/lib/action_view.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- # Copyright (c) 2004-2017 David Heinemeier Hansson # diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index 969d300bc1..637c8e7708 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/module/attr_internal" require "active_support/core_ext/module/attribute_accessors" require "active_support/ordered_options" diff --git a/actionview/lib/action_view/buffers.rb b/actionview/lib/action_view/buffers.rb index 089daa6d60..2a378fdc3c 100644 --- a/actionview/lib/action_view/buffers.rb +++ b/actionview/lib/action_view/buffers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/string/output_safety" module ActionView diff --git a/actionview/lib/action_view/context.rb b/actionview/lib/action_view/context.rb index 31aa73a0cf..e1b02fbde4 100644 --- a/actionview/lib/action_view/context.rb +++ b/actionview/lib/action_view/context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module CompiledTemplates #:nodoc: # holds compiled template code diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb index ee438f9311..02bd0545c2 100644 --- a/actionview/lib/action_view/dependency_tracker.rb +++ b/actionview/lib/action_view/dependency_tracker.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "concurrent/map" require_relative "path_set" diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index 00ff36c879..e404ebb6b6 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "concurrent/map" require_relative "dependency_tracker" require "monitor" diff --git a/actionview/lib/action_view/flows.rb b/actionview/lib/action_view/flows.rb index 6d5f57a570..ff44fa6619 100644 --- a/actionview/lib/action_view/flows.rb +++ b/actionview/lib/action_view/flows.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/string/output_safety" module ActionView diff --git a/actionview/lib/action_view/gem_version.rb b/actionview/lib/action_view/gem_version.rb index 92e21d7a4f..ed92490be7 100644 --- a/actionview/lib/action_view/gem_version.rb +++ b/actionview/lib/action_view/gem_version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView # Returns the version of the currently loaded Action View as a <tt>Gem::Version</tt> def self.gem_version diff --git a/actionview/lib/action_view/helpers.rb b/actionview/lib/action_view/helpers.rb index c1b4b4f84b..46f20c4277 100644 --- a/actionview/lib/action_view/helpers.rb +++ b/actionview/lib/action_view/helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/benchmarkable" module ActionView #:nodoc: diff --git a/actionview/lib/action_view/helpers/active_model_helper.rb b/actionview/lib/action_view/helpers/active_model_helper.rb index 4bb5788a16..46b514012c 100644 --- a/actionview/lib/action_view/helpers/active_model_helper.rb +++ b/actionview/lib/action_view/helpers/active_model_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/module/attribute_accessors" require "active_support/core_ext/enumerable" diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index cc8690e7bc..66e31978c6 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/array/extract_options" require "active_support/core_ext/hash/keys" require_relative "asset_url_helper" diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index 03bd1eb008..4c131aa27a 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "zlib" module ActionView diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb index 3538515aee..34245b5df5 100644 --- a/actionview/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "set" module ActionView diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb index b7c7324f31..cd213ad6a2 100644 --- a/actionview/lib/action_view/helpers/cache_helper.rb +++ b/actionview/lib/action_view/helpers/cache_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView # = Action View Cache Helper module Helpers diff --git a/actionview/lib/action_view/helpers/capture_helper.rb b/actionview/lib/action_view/helpers/capture_helper.rb index 719592b5c5..690ce6a0ee 100644 --- a/actionview/lib/action_view/helpers/capture_helper.rb +++ b/actionview/lib/action_view/helpers/capture_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/string/output_safety" module ActionView diff --git a/actionview/lib/action_view/helpers/controller_helper.rb b/actionview/lib/action_view/helpers/controller_helper.rb index e86cdca4e4..00d8b9665d 100644 --- a/actionview/lib/action_view/helpers/controller_helper.rb +++ b/actionview/lib/action_view/helpers/controller_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/module/attr_internal" module ActionView @@ -7,8 +9,11 @@ module ActionView module ControllerHelper #:nodoc: attr_internal :controller, :request - delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers, - :flash, :action_name, :controller_name, :controller_path, to: :controller + CONTROLLER_DELEGATES = [:request_forgery_protection_token, :params, + :session, :cookies, :response, :headers, :flash, :action_name, + :controller_name, :controller_path] + + delegate(*CONTROLLER_DELEGATES, to: :controller) def assign_controller(controller) if @_controller = controller @@ -21,6 +26,11 @@ module ActionView def logger controller.logger if controller.respond_to?(:logger) end + + def respond_to?(method_name, include_private = false) + return controller.respond_to?(method_name) if CONTROLLER_DELEGATES.include?(method_name.to_sym) + super + end end end end diff --git a/actionview/lib/action_view/helpers/csrf_helper.rb b/actionview/lib/action_view/helpers/csrf_helper.rb index b1319904f0..d1b83f87d3 100644 --- a/actionview/lib/action_view/helpers/csrf_helper.rb +++ b/actionview/lib/action_view/helpers/csrf_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView # = Action View CSRF Helper module Helpers diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 3fbed44f7e..339d582f76 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "date" require_relative "tag_helper" require "active_support/core_ext/array/extract_options" diff --git a/actionview/lib/action_view/helpers/debug_helper.rb b/actionview/lib/action_view/helpers/debug_helper.rb index f61ca2c9c2..232d688077 100644 --- a/actionview/lib/action_view/helpers/debug_helper.rb +++ b/actionview/lib/action_view/helpers/debug_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView # = Action View Debug Helper # diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 6b36c2272a..4eac086a87 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "cgi" require_relative "date_helper" require_relative "tag_helper" diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb index 0de3800a51..0f1a452652 100644 --- a/actionview/lib/action_view/helpers/form_options_helper.rb +++ b/actionview/lib/action_view/helpers/form_options_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "cgi" require "erb" require_relative "form_helper" diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index c8c6632781..91046acbf8 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "cgi" require_relative "tag_helper" require "active_support/core_ext/string/output_safety" diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb index 8806492572..cce7c1dcfd 100644 --- a/actionview/lib/action_view/helpers/javascript_helper.rb +++ b/actionview/lib/action_view/helpers/javascript_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "tag_helper" module ActionView diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb index b6bc5f4f6f..4b53b8fe6e 100644 --- a/actionview/lib/action_view/helpers/number_helper.rb +++ b/actionview/lib/action_view/helpers/number_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/hash/keys" require "active_support/core_ext/string/output_safety" require "active_support/number_helper" diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb index 25defd1276..279cde5e76 100644 --- a/actionview/lib/action_view/helpers/output_safety_helper.rb +++ b/actionview/lib/action_view/helpers/output_safety_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/string/output_safety" module ActionView #:nodoc: diff --git a/actionview/lib/action_view/helpers/record_tag_helper.rb b/actionview/lib/action_view/helpers/record_tag_helper.rb index ad59006386..4303de4209 100644 --- a/actionview/lib/action_view/helpers/record_tag_helper.rb +++ b/actionview/lib/action_view/helpers/record_tag_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module RecordTagHelper diff --git a/actionview/lib/action_view/helpers/rendering_helper.rb b/actionview/lib/action_view/helpers/rendering_helper.rb index 7d7f2393ff..2702fa28b2 100644 --- a/actionview/lib/action_view/helpers/rendering_helper.rb +++ b/actionview/lib/action_view/helpers/rendering_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers # = Action View Rendering diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb index 0abd5bc5dc..713b2fbb45 100644 --- a/actionview/lib/action_view/helpers/sanitize_helper.rb +++ b/actionview/lib/action_view/helpers/sanitize_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/object/try" require "rails-html-sanitizer" diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb index 306b71c85e..a64d7e396e 100644 --- a/actionview/lib/action_view/helpers/tag_helper.rb +++ b/actionview/lib/action_view/helpers/tag_helper.rb @@ -1,4 +1,4 @@ -# frozen-string-literal: true +# frozen_string_literal: true require "active_support/core_ext/string/output_safety" require "set" diff --git a/actionview/lib/action_view/helpers/tags.rb b/actionview/lib/action_view/helpers/tags.rb index a4f6eb0150..2552ebba4e 100644 --- a/actionview/lib/action_view/helpers/tags.rb +++ b/actionview/lib/action_view/helpers/tags.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags #:nodoc: diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb index 0895533a60..bbb8c4d224 100644 --- a/actionview/lib/action_view/helpers/tags/base.rb +++ b/actionview/lib/action_view/helpers/tags/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/check_box.rb b/actionview/lib/action_view/helpers/tags/check_box.rb index b9b988325d..c7d8cd0e97 100644 --- a/actionview/lib/action_view/helpers/tags/check_box.rb +++ b/actionview/lib/action_view/helpers/tags/check_box.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "checkable" module ActionView diff --git a/actionview/lib/action_view/helpers/tags/checkable.rb b/actionview/lib/action_view/helpers/tags/checkable.rb index 052e9df662..f2f4a655a3 100644 --- a/actionview/lib/action_view/helpers/tags/checkable.rb +++ b/actionview/lib/action_view/helpers/tags/checkable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb index ef37c1c342..91c1135d20 100644 --- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb +++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "collection_helpers" module ActionView diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb index 75d237eb35..e1ad11bff8 100644 --- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb +++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb index c7d28905d0..0b0482f74e 100644 --- a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb +++ b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "collection_helpers" module ActionView diff --git a/actionview/lib/action_view/helpers/tags/collection_select.rb b/actionview/lib/action_view/helpers/tags/collection_select.rb index 4365c714eb..ef1d4c8d1e 100644 --- a/actionview/lib/action_view/helpers/tags/collection_select.rb +++ b/actionview/lib/action_view/helpers/tags/collection_select.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/color_field.rb b/actionview/lib/action_view/helpers/tags/color_field.rb index b4bbe92746..8e698ef922 100644 --- a/actionview/lib/action_view/helpers/tags/color_field.rb +++ b/actionview/lib/action_view/helpers/tags/color_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/date_field.rb b/actionview/lib/action_view/helpers/tags/date_field.rb index c22be0db29..b17a907651 100644 --- a/actionview/lib/action_view/helpers/tags/date_field.rb +++ b/actionview/lib/action_view/helpers/tags/date_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/date_select.rb b/actionview/lib/action_view/helpers/tags/date_select.rb index 638c134deb..13c0515974 100644 --- a/actionview/lib/action_view/helpers/tags/date_select.rb +++ b/actionview/lib/action_view/helpers/tags/date_select.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/time/calculations" module ActionView diff --git a/actionview/lib/action_view/helpers/tags/datetime_field.rb b/actionview/lib/action_view/helpers/tags/datetime_field.rb index b3940c7e44..0556566130 100644 --- a/actionview/lib/action_view/helpers/tags/datetime_field.rb +++ b/actionview/lib/action_view/helpers/tags/datetime_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/datetime_local_field.rb b/actionview/lib/action_view/helpers/tags/datetime_local_field.rb index b4a74185d1..d8f8fd00d1 100644 --- a/actionview/lib/action_view/helpers/tags/datetime_local_field.rb +++ b/actionview/lib/action_view/helpers/tags/datetime_local_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/datetime_select.rb b/actionview/lib/action_view/helpers/tags/datetime_select.rb index 563de1840e..dc5570931d 100644 --- a/actionview/lib/action_view/helpers/tags/datetime_select.rb +++ b/actionview/lib/action_view/helpers/tags/datetime_select.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/email_field.rb b/actionview/lib/action_view/helpers/tags/email_field.rb index 7ce3ccb9bf..0c3b9224fa 100644 --- a/actionview/lib/action_view/helpers/tags/email_field.rb +++ b/actionview/lib/action_view/helpers/tags/email_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/file_field.rb b/actionview/lib/action_view/helpers/tags/file_field.rb index 476b820d84..0b1d9bb778 100644 --- a/actionview/lib/action_view/helpers/tags/file_field.rb +++ b/actionview/lib/action_view/helpers/tags/file_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/grouped_collection_select.rb b/actionview/lib/action_view/helpers/tags/grouped_collection_select.rb index 20e312dd0f..971db8e85d 100644 --- a/actionview/lib/action_view/helpers/tags/grouped_collection_select.rb +++ b/actionview/lib/action_view/helpers/tags/grouped_collection_select.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/hidden_field.rb b/actionview/lib/action_view/helpers/tags/hidden_field.rb index c3757c2461..e014bd3aef 100644 --- a/actionview/lib/action_view/helpers/tags/hidden_field.rb +++ b/actionview/lib/action_view/helpers/tags/hidden_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb index cab15ae201..56b48bbd62 100644 --- a/actionview/lib/action_view/helpers/tags/label.rb +++ b/actionview/lib/action_view/helpers/tags/label.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/month_field.rb b/actionview/lib/action_view/helpers/tags/month_field.rb index 4c0fb846ee..93b2bf11f0 100644 --- a/actionview/lib/action_view/helpers/tags/month_field.rb +++ b/actionview/lib/action_view/helpers/tags/month_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/number_field.rb b/actionview/lib/action_view/helpers/tags/number_field.rb index 4f95b1b4de..41c696423c 100644 --- a/actionview/lib/action_view/helpers/tags/number_field.rb +++ b/actionview/lib/action_view/helpers/tags/number_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/password_field.rb b/actionview/lib/action_view/helpers/tags/password_field.rb index 444ef65074..9f10f5236e 100644 --- a/actionview/lib/action_view/helpers/tags/password_field.rb +++ b/actionview/lib/action_view/helpers/tags/password_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/placeholderable.rb b/actionview/lib/action_view/helpers/tags/placeholderable.rb index cf7b117614..e9f7601e57 100644 --- a/actionview/lib/action_view/helpers/tags/placeholderable.rb +++ b/actionview/lib/action_view/helpers/tags/placeholderable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/radio_button.rb b/actionview/lib/action_view/helpers/tags/radio_button.rb index 782263ac5b..9e4f1c9e4b 100644 --- a/actionview/lib/action_view/helpers/tags/radio_button.rb +++ b/actionview/lib/action_view/helpers/tags/radio_button.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "checkable" module ActionView diff --git a/actionview/lib/action_view/helpers/tags/range_field.rb b/actionview/lib/action_view/helpers/tags/range_field.rb index f98ae88043..66d1bbac5b 100644 --- a/actionview/lib/action_view/helpers/tags/range_field.rb +++ b/actionview/lib/action_view/helpers/tags/range_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/search_field.rb b/actionview/lib/action_view/helpers/tags/search_field.rb index a848aeabfa..f209348904 100644 --- a/actionview/lib/action_view/helpers/tags/search_field.rb +++ b/actionview/lib/action_view/helpers/tags/search_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/select.rb b/actionview/lib/action_view/helpers/tags/select.rb index 380f7a8c4e..0de4139101 100644 --- a/actionview/lib/action_view/helpers/tags/select.rb +++ b/actionview/lib/action_view/helpers/tags/select.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/tel_field.rb b/actionview/lib/action_view/helpers/tags/tel_field.rb index 987bb9e67a..ab1caaac48 100644 --- a/actionview/lib/action_view/helpers/tags/tel_field.rb +++ b/actionview/lib/action_view/helpers/tags/tel_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/text_area.rb b/actionview/lib/action_view/helpers/tags/text_area.rb index 1058fdf55f..d8460a4be4 100644 --- a/actionview/lib/action_view/helpers/tags/text_area.rb +++ b/actionview/lib/action_view/helpers/tags/text_area.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "placeholderable" module ActionView diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb index 1d55105587..e4c5a49069 100644 --- a/actionview/lib/action_view/helpers/tags/text_field.rb +++ b/actionview/lib/action_view/helpers/tags/text_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "placeholderable" module ActionView diff --git a/actionview/lib/action_view/helpers/tags/time_field.rb b/actionview/lib/action_view/helpers/tags/time_field.rb index 0e90a3aed7..9384a83a3e 100644 --- a/actionview/lib/action_view/helpers/tags/time_field.rb +++ b/actionview/lib/action_view/helpers/tags/time_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/time_select.rb b/actionview/lib/action_view/helpers/tags/time_select.rb index 0b06311d25..ba3dcb64e3 100644 --- a/actionview/lib/action_view/helpers/tags/time_select.rb +++ b/actionview/lib/action_view/helpers/tags/time_select.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/time_zone_select.rb b/actionview/lib/action_view/helpers/tags/time_zone_select.rb index 80d165ec7e..3b6bcacce0 100644 --- a/actionview/lib/action_view/helpers/tags/time_zone_select.rb +++ b/actionview/lib/action_view/helpers/tags/time_zone_select.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/translator.rb b/actionview/lib/action_view/helpers/tags/translator.rb index ced835eaa8..fcf96d2c9c 100644 --- a/actionview/lib/action_view/helpers/tags/translator.rb +++ b/actionview/lib/action_view/helpers/tags/translator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/url_field.rb b/actionview/lib/action_view/helpers/tags/url_field.rb index d76340178d..395fec67e7 100644 --- a/actionview/lib/action_view/helpers/tags/url_field.rb +++ b/actionview/lib/action_view/helpers/tags/url_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/tags/week_field.rb b/actionview/lib/action_view/helpers/tags/week_field.rb index 835d1667d7..572535d1d6 100644 --- a/actionview/lib/action_view/helpers/tags/week_field.rb +++ b/actionview/lib/action_view/helpers/tags/week_field.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Helpers module Tags # :nodoc: diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb index bc922f9ce8..3044a2c0ef 100644 --- a/actionview/lib/action_view/helpers/text_helper.rb +++ b/actionview/lib/action_view/helpers/text_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/string/filters" require "active_support/core_ext/array/extract_options" diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index b10cfadaed..75c1161de1 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "tag_helper" require "active_support/core_ext/string/access" require "i18n/exceptions" diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 644e1e4391..2d5aac6dc7 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "javascript_helper" require "active_support/core_ext/array/access" require "active_support/core_ext/hash/keys" diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb index b62fde30e8..d074654b49 100644 --- a/actionview/lib/action_view/layouts.rb +++ b/actionview/lib/action_view/layouts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "rendering" require "active_support/core_ext/module/remove_method" diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index ce5493c01b..acc0f57f1d 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "concurrent/map" require "active_support/core_ext/module/remove_method" require "active_support/core_ext/module/attribute_accessors" diff --git a/actionview/lib/action_view/model_naming.rb b/actionview/lib/action_view/model_naming.rb index b6ed13424e..23cca8d607 100644 --- a/actionview/lib/action_view/model_naming.rb +++ b/actionview/lib/action_view/model_naming.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module ModelNaming #:nodoc: # Converts the given object to an ActiveModel compliant one. diff --git a/actionview/lib/action_view/path_set.rb b/actionview/lib/action_view/path_set.rb index 6688519ffd..691b53e2da 100644 --- a/actionview/lib/action_view/path_set.rb +++ b/actionview/lib/action_view/path_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView #:nodoc: # = Action View PathSet # diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb index 61678933e9..b22347c55c 100644 --- a/actionview/lib/action_view/railtie.rb +++ b/actionview/lib/action_view/railtie.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "action_view" require "rails" diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb index 6805513347..b34a793c89 100644 --- a/actionview/lib/action_view/record_identifier.rb +++ b/actionview/lib/action_view/record_identifier.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/module" require_relative "model_naming" diff --git a/actionview/lib/action_view/renderer/abstract_renderer.rb b/actionview/lib/action_view/renderer/abstract_renderer.rb index 0b315eb569..20b2523cac 100644 --- a/actionview/lib/action_view/renderer/abstract_renderer.rb +++ b/actionview/lib/action_view/renderer/abstract_renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView # This class defines the interface for a renderer. Each class that # subclasses +AbstractRenderer+ is used by the base +Renderer+ class to diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 77f5084686..f548fc24ed 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "concurrent/map" require_relative "partial_renderer/collection_caching" diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb index 32663fb80d..db52919e91 100644 --- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb +++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module CollectionCaching # :nodoc: extend ActiveSupport::Concern diff --git a/actionview/lib/action_view/renderer/renderer.rb b/actionview/lib/action_view/renderer/renderer.rb index bcdeb85d30..3f3a97529d 100644 --- a/actionview/lib/action_view/renderer/renderer.rb +++ b/actionview/lib/action_view/renderer/renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView # This is the main entry point for rendering. It basically delegates # to other objects like TemplateRenderer and PartialRenderer which diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb index e54f9b8977..ca49eb1144 100644 --- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require "fiber" module ActionView diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index 54317199de..ce8908924a 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/object/try" module ActionView diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index 9bee76a1f7..2648f9153f 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "view_paths" module ActionView diff --git a/actionview/lib/action_view/routing_url_for.rb b/actionview/lib/action_view/routing_url_for.rb index 687ba7c1b4..fd563f34a9 100644 --- a/actionview/lib/action_view/routing_url_for.rb +++ b/actionview/lib/action_view/routing_url_for.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "action_dispatch/routing/polymorphic_routes" module ActionView diff --git a/actionview/lib/action_view/tasks/cache_digests.rake b/actionview/lib/action_view/tasks/cache_digests.rake index d30b3f7797..dd8e94bd88 100644 --- a/actionview/lib/action_view/tasks/cache_digests.rake +++ b/actionview/lib/action_view/tasks/cache_digests.rake @@ -1,3 +1,5 @@ +# frozen_string_literal: true + namespace :cache_digests do desc "Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)" task nested_dependencies: :environment do diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb index cc90477190..2b0b25817b 100644 --- a/actionview/lib/action_view/template/error.rb +++ b/actionview/lib/action_view/template/error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/enumerable" module ActionView diff --git a/actionview/lib/action_view/template/handlers.rb b/actionview/lib/action_view/template/handlers.rb index f4301f6f07..b3df0aa606 100644 --- a/actionview/lib/action_view/template/handlers.rb +++ b/actionview/lib/action_view/template/handlers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView #:nodoc: # = Action View Template Handlers class Template diff --git a/actionview/lib/action_view/template/handlers/builder.rb b/actionview/lib/action_view/template/handlers/builder.rb index 67ad78133d..61492ce448 100644 --- a/actionview/lib/action_view/template/handlers/builder.rb +++ b/actionview/lib/action_view/template/handlers/builder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Template::Handlers class Builder diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb index 48c2e22a89..c41de62e52 100644 --- a/actionview/lib/action_view/template/handlers/erb.rb +++ b/actionview/lib/action_view/template/handlers/erb.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView class Template module Handlers diff --git a/actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb b/actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb index 427ea20064..00c7d7cc7d 100644 --- a/actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb +++ b/actionview/lib/action_view/template/handlers/erb/deprecated_erubis.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ::ActiveSupport::Deprecation.warn("ActionView::Template::Handlers::Erubis is deprecated and will be removed from Rails 5.2. Switch to ActionView::Template::Handlers::ERB::Erubi instead.") module ActionView diff --git a/actionview/lib/action_view/template/handlers/erb/erubi.rb b/actionview/lib/action_view/template/handlers/erb/erubi.rb index 755cc84015..db75f028ed 100644 --- a/actionview/lib/action_view/template/handlers/erb/erubi.rb +++ b/actionview/lib/action_view/template/handlers/erb/erubi.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "erubi" module ActionView diff --git a/actionview/lib/action_view/template/handlers/erb/erubis.rb b/actionview/lib/action_view/template/handlers/erb/erubis.rb index f3c35e1aec..dc3bf1ff27 100644 --- a/actionview/lib/action_view/template/handlers/erb/erubis.rb +++ b/actionview/lib/action_view/template/handlers/erb/erubis.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + gem "erubis" require "erubis" diff --git a/actionview/lib/action_view/template/handlers/html.rb b/actionview/lib/action_view/template/handlers/html.rb index ccaa8d1469..27004a318c 100644 --- a/actionview/lib/action_view/template/handlers/html.rb +++ b/actionview/lib/action_view/template/handlers/html.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Template::Handlers class Html < Raw diff --git a/actionview/lib/action_view/template/handlers/raw.rb b/actionview/lib/action_view/template/handlers/raw.rb index e7519e94f9..5cd23a0060 100644 --- a/actionview/lib/action_view/template/handlers/raw.rb +++ b/actionview/lib/action_view/template/handlers/raw.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module Template::Handlers class Raw diff --git a/actionview/lib/action_view/template/html.rb b/actionview/lib/action_view/template/html.rb index 0ffae10432..540597efa1 100644 --- a/actionview/lib/action_view/template/html.rb +++ b/actionview/lib/action_view/template/html.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView #:nodoc: # = Action View HTML Template class Template diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 0ccf398d9a..708dee3164 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "pathname" require "active_support/core_ext/class" require "active_support/core_ext/module/attribute_accessors" diff --git a/actionview/lib/action_view/template/text.rb b/actionview/lib/action_view/template/text.rb index 380528d6ef..f070005881 100644 --- a/actionview/lib/action_view/template/text.rb +++ b/actionview/lib/action_view/template/text.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView #:nodoc: # = Action View Text Template class Template diff --git a/actionview/lib/action_view/template/types.rb b/actionview/lib/action_view/template/types.rb index 21959a3798..f0f37c9722 100644 --- a/actionview/lib/action_view/template/types.rb +++ b/actionview/lib/action_view/template/types.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/module/attribute_accessors" module ActionView diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb index 424a86ba3e..8542978e34 100644 --- a/actionview/lib/action_view/test_case.rb +++ b/actionview/lib/action_view/test_case.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/module/remove_method" require "action_controller" require "action_controller/test_case" diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb index 5e853311e6..92fdb24a5e 100644 --- a/actionview/lib/action_view/testing/resolvers.rb +++ b/actionview/lib/action_view/testing/resolvers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "../template/resolver" module ActionView #:nodoc: diff --git a/actionview/lib/action_view/version.rb b/actionview/lib/action_view/version.rb index 315404864d..be53797a14 100644 --- a/actionview/lib/action_view/version.rb +++ b/actionview/lib/action_view/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "gem_version" module ActionView diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb index 938f0fc17f..d5694d77f4 100644 --- a/actionview/lib/action_view/view_paths.rb +++ b/actionview/lib/action_view/view_paths.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActionView module ViewPaths extend ActiveSupport::Concern diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index a7d706c5e1..c98270bd12 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + $:.unshift File.expand_path("lib", __dir__) $:.unshift File.expand_path("fixtures/helpers", __dir__) $:.unshift File.expand_path("fixtures/alternate_helpers", __dir__) diff --git a/actionview/test/actionpack/abstract/abstract_controller_test.rb b/actionview/test/actionpack/abstract/abstract_controller_test.rb index 8f65a61493..2bf2ddcc70 100644 --- a/actionview/test/actionpack/abstract/abstract_controller_test.rb +++ b/actionview/test/actionpack/abstract/abstract_controller_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "set" diff --git a/actionview/test/actionpack/abstract/helper_test.rb b/actionview/test/actionpack/abstract/helper_test.rb index 13922e4485..480ff60ba2 100644 --- a/actionview/test/actionpack/abstract/helper_test.rb +++ b/actionview/test/actionpack/abstract/helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" ActionController::Base.helpers_path = File.expand_path("../../fixtures/helpers", __dir__) diff --git a/actionview/test/actionpack/abstract/layouts_test.rb b/actionview/test/actionpack/abstract/layouts_test.rb index 4ece992597..1146e6f64b 100644 --- a/actionview/test/actionpack/abstract/layouts_test.rb +++ b/actionview/test/actionpack/abstract/layouts_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" module AbstractControllerTests diff --git a/actionview/test/actionpack/abstract/render_test.rb b/actionview/test/actionpack/abstract/render_test.rb index 8b0c54fb77..d863548a5c 100644 --- a/actionview/test/actionpack/abstract/render_test.rb +++ b/actionview/test/actionpack/abstract/render_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" module AbstractController diff --git a/actionview/test/actionpack/controller/capture_test.rb b/actionview/test/actionpack/controller/capture_test.rb index cc3a23c60c..09309e5b6d 100644 --- a/actionview/test/actionpack/controller/capture_test.rb +++ b/actionview/test/actionpack/controller/capture_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/logger" diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb index b3e0329f57..ff66ff2a1a 100644 --- a/actionview/test/actionpack/controller/layout_test.rb +++ b/actionview/test/actionpack/controller/layout_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/core_ext/array/extract_options" diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index 6528169312..9df2a73448 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_model" require "controller/fake_models" diff --git a/actionview/test/actionpack/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb index 4c58b959a9..45c662f0ce 100644 --- a/actionview/test/actionpack/controller/view_paths_test.rb +++ b/actionview/test/actionpack/controller/view_paths_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class ViewLoadPathsTest < ActionController::TestCase diff --git a/actionview/test/active_record_unit.rb b/actionview/test/active_record_unit.rb index 901c0e2b3e..b39ecd8813 100644 --- a/actionview/test/active_record_unit.rb +++ b/actionview/test/active_record_unit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" # Define the essentials diff --git a/actionview/test/activerecord/controller_runtime_test.rb b/actionview/test/activerecord/controller_runtime_test.rb index 1cec5072c0..42b171ea07 100644 --- a/actionview/test/activerecord/controller_runtime_test.rb +++ b/actionview/test/activerecord/controller_runtime_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_record_unit" require "active_record/railties/controller_runtime" require "fixtures/project" diff --git a/actionview/test/activerecord/debug_helper_test.rb b/actionview/test/activerecord/debug_helper_test.rb index 06ae555a03..4be1023733 100644 --- a/actionview/test/activerecord/debug_helper_test.rb +++ b/actionview/test/activerecord/debug_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_record_unit" require "nokogiri" diff --git a/actionview/test/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb index 9949c3fde0..1472ee8def 100644 --- a/actionview/test/activerecord/form_helper_activerecord_test.rb +++ b/actionview/test/activerecord/form_helper_activerecord_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_record_unit" require "fixtures/project" require "fixtures/developer" diff --git a/actionview/test/activerecord/polymorphic_routes_test.rb b/actionview/test/activerecord/polymorphic_routes_test.rb index b2e0fb08c4..4b931f793f 100644 --- a/actionview/test/activerecord/polymorphic_routes_test.rb +++ b/actionview/test/activerecord/polymorphic_routes_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_record_unit" require "fixtures/project" diff --git a/actionview/test/activerecord/relation_cache_test.rb b/actionview/test/activerecord/relation_cache_test.rb index d12c426586..bd0cd10eaf 100644 --- a/actionview/test/activerecord/relation_cache_test.rb +++ b/actionview/test/activerecord/relation_cache_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_record_unit" class RelationCacheTest < ActionView::TestCase diff --git a/actionview/test/activerecord/render_partial_with_record_identification_test.rb b/actionview/test/activerecord/render_partial_with_record_identification_test.rb index 60c3ab3045..367d2c3174 100644 --- a/actionview/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionview/test/activerecord/render_partial_with_record_identification_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_record_unit" class RenderPartialWithRecordIdentificationController < ActionController::Base diff --git a/actionview/test/fixtures/company.rb b/actionview/test/fixtures/company.rb index 9f527acdd8..93afdd5472 100644 --- a/actionview/test/fixtures/company.rb +++ b/actionview/test/fixtures/company.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Company < ActiveRecord::Base has_one :mascot self.sequence_name = :companies_nonstd_seq diff --git a/actionview/test/fixtures/developer.rb b/actionview/test/fixtures/developer.rb index 1a686a33ce..cb7ee49eed 100644 --- a/actionview/test/fixtures/developer.rb +++ b/actionview/test/fixtures/developer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Developer < ActiveRecord::Base has_and_belongs_to_many :projects has_many :replies diff --git a/actionview/test/fixtures/helpers/abc_helper.rb b/actionview/test/fixtures/helpers/abc_helper.rb index cf2774bb5f..999b9b5c6e 100644 --- a/actionview/test/fixtures/helpers/abc_helper.rb +++ b/actionview/test/fixtures/helpers/abc_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module AbcHelper def bare_a() end end diff --git a/actionview/test/fixtures/helpers/helpery_test_helper.rb b/actionview/test/fixtures/helpers/helpery_test_helper.rb index a4f2951efa..9836143848 100644 --- a/actionview/test/fixtures/helpers/helpery_test_helper.rb +++ b/actionview/test/fixtures/helpers/helpery_test_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module HelperyTestHelper def helpery_test "Default" diff --git a/actionview/test/fixtures/helpers_missing/invalid_require_helper.rb b/actionview/test/fixtures/helpers_missing/invalid_require_helper.rb index 6ac6677daa..c77121046d 100644 --- a/actionview/test/fixtures/helpers_missing/invalid_require_helper.rb +++ b/actionview/test/fixtures/helpers_missing/invalid_require_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "very_invalid_file_name" module InvalidRequireHelper diff --git a/actionview/test/fixtures/mascot.rb b/actionview/test/fixtures/mascot.rb index 1c0bd7c8fd..26a2c7bbe1 100644 --- a/actionview/test/fixtures/mascot.rb +++ b/actionview/test/fixtures/mascot.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Mascot < ActiveRecord::Base belongs_to :company end diff --git a/actionview/test/fixtures/project.rb b/actionview/test/fixtures/project.rb index 404b12cbab..019ddb7aef 100644 --- a/actionview/test/fixtures/project.rb +++ b/actionview/test/fixtures/project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Project < ActiveRecord::Base has_and_belongs_to_many :developers, -> { uniq } diff --git a/actionview/test/fixtures/reply.rb b/actionview/test/fixtures/reply.rb index 047522c55b..b2b662e1b5 100644 --- a/actionview/test/fixtures/reply.rb +++ b/actionview/test/fixtures/reply.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Reply < ActiveRecord::Base scope :base, -> { all } belongs_to :topic, -> { includes(:replies) } diff --git a/actionview/test/fixtures/topic.rb b/actionview/test/fixtures/topic.rb index 48a3dfba88..ff194ce567 100644 --- a/actionview/test/fixtures/topic.rb +++ b/actionview/test/fixtures/topic.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Topic < ActiveRecord::Base has_many :replies, dependent: :destroy end diff --git a/actionview/test/lib/controller/fake_models.rb b/actionview/test/lib/controller/fake_models.rb index 5250101220..f8b7ddaecc 100644 --- a/actionview/test/lib/controller/fake_models.rb +++ b/actionview/test/lib/controller/fake_models.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_model" Customer = Struct.new(:name, :id) do diff --git a/actionview/test/template/active_model_helper_test.rb b/actionview/test/template/active_model_helper_test.rb index 6b63aa25a5..a929d8dc3d 100644 --- a/actionview/test/template/active_model_helper_test.rb +++ b/actionview/test/template/active_model_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class ActiveModelHelperTest < ActionView::TestCase diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index d1190b1bd7..4d312d3b6d 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/ordered_options" diff --git a/actionview/test/template/atom_feed_helper_test.rb b/actionview/test/template/atom_feed_helper_test.rb index 7fa5e042fb..1be20dcaae 100644 --- a/actionview/test/template/atom_feed_helper_test.rb +++ b/actionview/test/template/atom_feed_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" Scroll = Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at) do diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb index 7f37523eeb..8a1c00fd00 100644 --- a/actionview/test/template/capture_helper_test.rb +++ b/actionview/test/template/capture_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class CaptureHelperTest < ActionView::TestCase diff --git a/actionview/test/template/compiled_templates_test.rb b/actionview/test/template/compiled_templates_test.rb index adb2be9be4..3cd6448e38 100644 --- a/actionview/test/template/compiled_templates_test.rb +++ b/actionview/test/template/compiled_templates_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class CompiledTemplatesTest < ActiveSupport::TestCase diff --git a/actionview/test/template/controller_helper_test.rb b/actionview/test/template/controller_helper_test.rb index 8dd0cedb75..46d20c188c 100644 --- a/actionview/test/template/controller_helper_test.rb +++ b/actionview/test/template/controller_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class ControllerHelperTest < ActionView::TestCase @@ -18,4 +20,15 @@ class ControllerHelperTest < ActionView::TestCase assert_nil default_form_builder end + + def test_respond_to + @controller = OpenStruct.new + assign_controller(@controller) + assert_not respond_to?(:params) + assert respond_to?(:assign_controller) + + @controller.params = {} + assert respond_to?(:params) + assert respond_to?(:assign_controller) + end end diff --git a/actionview/test/template/date_helper_i18n_test.rb b/actionview/test/template/date_helper_i18n_test.rb index 207c8a683e..60303b4c91 100644 --- a/actionview/test/template/date_helper_i18n_test.rb +++ b/actionview/test/template/date_helper_i18n_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class DateHelperDistanceOfTimeInWordsI18nTests < ActiveSupport::TestCase diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb index b9b8194e69..35a08ea672 100644 --- a/actionview/test/template/date_helper_test.rb +++ b/actionview/test/template/date_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class DateHelperTest < ActionView::TestCase diff --git a/actionview/test/template/dependency_tracker_test.rb b/actionview/test/template/dependency_tracker_test.rb index 89917035ff..ef7aeac039 100644 --- a/actionview/test/template/dependency_tracker_test.rb +++ b/actionview/test/template/dependency_tracker_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "action_view/dependency_tracker" diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb index de04f3f25d..928b1ac7dd 100644 --- a/actionview/test/template/digestor_test.rb +++ b/actionview/test/template/digestor_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "fileutils" require "action_view/dependency_tracker" diff --git a/actionview/test/template/erb/deprecated_erubis_implementation_test.rb b/actionview/test/template/erb/deprecated_erubis_implementation_test.rb index aaf99f85c0..ea088e7cfc 100644 --- a/actionview/test/template/erb/deprecated_erubis_implementation_test.rb +++ b/actionview/test/template/erb/deprecated_erubis_implementation_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" module ERBTest diff --git a/actionview/test/template/erb/form_for_test.rb b/actionview/test/template/erb/form_for_test.rb index e722b40a9a..b6ecf003a5 100644 --- a/actionview/test/template/erb/form_for_test.rb +++ b/actionview/test/template/erb/form_for_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "template/erb/helper" diff --git a/actionview/test/template/erb/helper.rb b/actionview/test/template/erb/helper.rb index bc1eedc15f..57d6cb1be3 100644 --- a/actionview/test/template/erb/helper.rb +++ b/actionview/test/template/erb/helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ERBTest class ViewContext include ActionView::Helpers::UrlHelper diff --git a/actionview/test/template/erb/tag_helper_test.rb b/actionview/test/template/erb/tag_helper_test.rb index 233f37c48a..24a7592950 100644 --- a/actionview/test/template/erb/tag_helper_test.rb +++ b/actionview/test/template/erb/tag_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "template/erb/helper" diff --git a/actionview/test/template/erb_util_test.rb b/actionview/test/template/erb_util_test.rb index 4412ea37fa..8b804105f4 100644 --- a/actionview/test/template/erb_util_test.rb +++ b/actionview/test/template/erb_util_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/json" diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb index 6160524eb3..aa72621c7d 100644 --- a/actionview/test/template/form_collections_helper_test.rb +++ b/actionview/test/template/form_collections_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" Category = Struct.new(:id, :name) diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb index c1a8181204..c7d49070ce 100644 --- a/actionview/test/template/form_helper/form_with_test.rb +++ b/actionview/test/template/form_helper/form_with_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "controller/fake_models" diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index ec2e6b0959..8d689aebeb 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "controller/fake_models" diff --git a/actionview/test/template/form_options_helper_i18n_test.rb b/actionview/test/template/form_options_helper_i18n_test.rb index a1048fbb89..21295fa547 100644 --- a/actionview/test/template/form_options_helper_i18n_test.rb +++ b/actionview/test/template/form_options_helper_i18n_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class FormOptionsHelperI18nTests < ActionView::TestCase diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb index 792481f432..56a8a125e5 100644 --- a/actionview/test/template/form_options_helper_test.rb +++ b/actionview/test/template/form_options_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class Map < Hash diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index 7ac15db9fe..62b67b0435 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class FormTagHelperTest < ActionView::TestCase diff --git a/actionview/test/template/html_test.rb b/actionview/test/template/html_test.rb index 60f4b0aee6..5cdff74d60 100644 --- a/actionview/test/template/html_test.rb +++ b/actionview/test/template/html_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class HTMLTest < ActiveSupport::TestCase diff --git a/actionview/test/template/javascript_helper_test.rb b/actionview/test/template/javascript_helper_test.rb index 67747a7a83..4478c9f4ab 100644 --- a/actionview/test/template/javascript_helper_test.rb +++ b/actionview/test/template/javascript_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class JavaScriptHelperTest < ActionView::TestCase diff --git a/actionview/test/template/log_subscriber_test.rb b/actionview/test/template/log_subscriber_test.rb index 4c9f84f277..a4d89ba0d1 100644 --- a/actionview/test/template/log_subscriber_test.rb +++ b/actionview/test/template/log_subscriber_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/log_subscriber/test_helper" require "action_view/log_subscriber" diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb index b47d92df34..402ee9b6ae 100644 --- a/actionview/test/template/lookup_context_test.rb +++ b/actionview/test/template/lookup_context_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "abstract_controller/rendering" diff --git a/actionview/test/template/number_helper_test.rb b/actionview/test/template/number_helper_test.rb index 678120a9c9..2b671a6685 100644 --- a/actionview/test/template/number_helper_test.rb +++ b/actionview/test/template/number_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class NumberHelperTest < ActionView::TestCase diff --git a/actionview/test/template/output_safety_helper_test.rb b/actionview/test/template/output_safety_helper_test.rb index 537b4393ee..b5e9a77105 100644 --- a/actionview/test/template/output_safety_helper_test.rb +++ b/actionview/test/template/output_safety_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class OutputSafetyHelperTest < ActionView::TestCase diff --git a/actionview/test/template/partial_iteration_test.rb b/actionview/test/template/partial_iteration_test.rb index 3ebf3b550a..06bbdabac0 100644 --- a/actionview/test/template/partial_iteration_test.rb +++ b/actionview/test/template/partial_iteration_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "action_view/renderer/partial_renderer" diff --git a/actionview/test/template/record_identifier_test.rb b/actionview/test/template/record_identifier_test.rb index ce446715fd..29012e943d 100644 --- a/actionview/test/template/record_identifier_test.rb +++ b/actionview/test/template/record_identifier_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "controller/fake_models" diff --git a/actionview/test/template/record_tag_helper_test.rb b/actionview/test/template/record_tag_helper_test.rb index 3685230558..7bbbfccdd0 100644 --- a/actionview/test/template/record_tag_helper_test.rb +++ b/actionview/test/template/record_tag_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class RecordTagPost diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index 1fd8b4fe1a..8782eb4430 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "controller/fake_models" diff --git a/actionview/test/template/resolver_cache_test.rb b/actionview/test/template/resolver_cache_test.rb index 0ecfccd375..8a5db1346a 100644 --- a/actionview/test/template/resolver_cache_test.rb +++ b/actionview/test/template/resolver_cache_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class ResolverCacheTest < ActiveSupport::TestCase diff --git a/actionview/test/template/resolver_patterns_test.rb b/actionview/test/template/resolver_patterns_test.rb index 8e21f4b828..1e1a4c5063 100644 --- a/actionview/test/template/resolver_patterns_test.rb +++ b/actionview/test/template/resolver_patterns_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class ResolverPatternsTest < ActiveSupport::TestCase diff --git a/actionview/test/template/sanitize_helper_test.rb b/actionview/test/template/sanitize_helper_test.rb index 4d4ed3c35c..c7714cf205 100644 --- a/actionview/test/template/sanitize_helper_test.rb +++ b/actionview/test/template/sanitize_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" # The exhaustive tests are in the rails-html-sanitizer gem. diff --git a/actionview/test/template/streaming_render_test.rb b/actionview/test/template/streaming_render_test.rb index 41b9063417..23edf7b538 100644 --- a/actionview/test/template/streaming_render_test.rb +++ b/actionview/test/template/streaming_render_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class TestController < ActionController::Base diff --git a/actionview/test/template/tag_helper_test.rb b/actionview/test/template/tag_helper_test.rb index f1e5946e14..8c57803796 100644 --- a/actionview/test/template/tag_helper_test.rb +++ b/actionview/test/template/tag_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class TagHelperTest < ActionView::TestCase diff --git a/actionview/test/template/template_error_test.rb b/actionview/test/template/template_error_test.rb index 54c1d53b60..c4dc88e4aa 100644 --- a/actionview/test/template/template_error_test.rb +++ b/actionview/test/template/template_error_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class TemplateErrorTest < ActiveSupport::TestCase diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb index d3c08634bc..3dc14e36e0 100644 --- a/actionview/test/template/template_test.rb +++ b/actionview/test/template/template_test.rb @@ -1,4 +1,5 @@ # encoding: US-ASCII +# frozen_string_literal: true require "abstract_unit" require "logger" diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index 3deddd5706..f2f64ffa71 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "rails/engine" diff --git a/actionview/test/template/test_test.rb b/actionview/test/template/test_test.rb index 52cac77bc5..78ba536dfc 100644 --- a/actionview/test/template/test_test.rb +++ b/actionview/test/template/test_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" module PeopleHelper diff --git a/actionview/test/template/testing/fixture_resolver_test.rb b/actionview/test/template/testing/fixture_resolver_test.rb index effe453fc0..9954e3500d 100644 --- a/actionview/test/template/testing/fixture_resolver_test.rb +++ b/actionview/test/template/testing/fixture_resolver_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class FixtureResolverTest < ActiveSupport::TestCase diff --git a/actionview/test/template/testing/null_resolver_test.rb b/actionview/test/template/testing/null_resolver_test.rb index 5346fd3368..53364c1d90 100644 --- a/actionview/test/template/testing/null_resolver_test.rb +++ b/actionview/test/template/testing/null_resolver_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class NullResolverTest < ActiveSupport::TestCase diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb index 21ee4cc8e5..adb923d93b 100644 --- a/actionview/test/template/text_helper_test.rb +++ b/actionview/test/template/text_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class TextHelperTest < ActionView::TestCase diff --git a/actionview/test/template/text_test.rb b/actionview/test/template/text_test.rb index 72ffd5f3be..0c6470df21 100644 --- a/actionview/test/template/text_test.rb +++ b/actionview/test/template/text_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class TextTest < ActiveSupport::TestCase diff --git a/actionview/test/template/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb index 1cfc13f337..8956a584ff 100644 --- a/actionview/test/template/translation_helper_test.rb +++ b/actionview/test/template/translation_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" module I18n diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index a087c5c0cd..0cd0386cac 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class UrlHelperTest < ActiveSupport::TestCase diff --git a/actionview/test/ujs/config.ru b/actionview/test/ujs/config.ru index 213a41127a..7cd3a16acb 100644 --- a/actionview/test/ujs/config.ru +++ b/actionview/test/ujs/config.ru @@ -1,3 +1,5 @@ +# frozen_string_literal: true + $LOAD_PATH.unshift __dir__ require "server" diff --git a/actionview/test/ujs/server.rb b/actionview/test/ujs/server.rb index 7deb208af0..7d1bab4b2a 100644 --- a/actionview/test/ujs/server.rb +++ b/actionview/test/ujs/server.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "rack" require "rails" require "action_controller/railtie" diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ea9f860b95..afc459ef68 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Fix `COUNT(DISTINCT ...)` with `ORDER BY` and `LIMIT` to keep the existing select list. + + *Ryuta Kamizono* + * When a `has_one` association is destroyed by `dependent: destroy`, `destroyed_by_association` will now be set to the reflection, matching the behaviour of `has_many` associations. diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index 7b303e923b..9904ee4bed 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -34,9 +34,7 @@ module ActiveRecord::Associations::Builder # :nodoc: foreign_key = reflection.foreign_key cache_column = reflection.counter_cache_column - if (@_after_create_counter_called ||= false) - @_after_create_counter_called = false - elsif (@_after_replace_counter_called ||= false) + if (@_after_replace_counter_called ||= false) @_after_replace_counter_called = false elsif saved_change_to_attribute?(foreign_key) && !new_record? if reflection.polymorphic? diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 938d2645ca..89ce00f98e 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -154,7 +154,7 @@ module ActiveRecord stmt.from scope.klass.arel_table stmt.wheres = arel.constraints - count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes) + count = scope.klass.connection.delete(stmt, "SQL") end when :nullify count = scope.update_all(source_reflection.foreign_key => nil) diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index 42014653e8..30dbdb7fa5 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -23,11 +23,10 @@ module ActiveRecord super && reflection == other.reflection end - JoinInformation = Struct.new :joins, :binds + JoinInformation = Struct.new :joins def join_constraints(foreign_table, foreign_klass, join_type, tables, chain) joins = [] - binds = [] tables = tables.reverse # The chain starts with the target table, but we want to end with it here (makes @@ -43,7 +42,6 @@ module ActiveRecord join_scope = reflection.join_scope(table, foreign_klass) if join_scope.arel.constraints.any? - binds.concat join_scope.bound_attributes joins.concat join_scope.arel.join_sources right = joins.last.right right.expr = right.expr.and(join_scope.arel.constraints) @@ -53,7 +51,7 @@ module ActiveRecord foreign_table, foreign_klass = table, klass end - JoinInformation.new joins, binds + JoinInformation.new joins end def table diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb index 62948225af..b1937a3c68 100644 --- a/activerecord/lib/active_record/collection_cache_key.rb +++ b/activerecord/lib/active_record/collection_cache_key.rb @@ -16,7 +16,7 @@ module ActiveRecord column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}" select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp" - if collection.limit_value || collection.offset_value + if collection.has_limit_or_offset? query = collection.spawn query.select_values = [column] subquery_alias = "subquery_for_cache_key" @@ -29,7 +29,7 @@ module ActiveRecord arel = query.arel end - result = connection.select_one(arel, nil, query.bound_attributes) + result = connection.select_one(arel, nil) if result.blank? size = 0 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 f2715cdab0..00e54edac0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -9,30 +9,37 @@ module ActiveRecord end # Converts an arel AST to SQL - def to_sql(arel, binds = []) - if arel.respond_to?(:ast) - collected = visitor.accept(arel.ast, collector) - collected.compile(binds, self).freeze + def to_sql(arel_or_sql_string, binds = []) + if arel_or_sql_string.respond_to?(:ast) + unless binds.empty? + raise "Passing bind parameters with an arel AST is forbidden. " \ + "The values must be stored on the AST directly" + end + sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value + [sql.freeze, binds || []] else - arel.dup.freeze + [arel_or_sql_string.dup.freeze, binds] end end # This is used in the StatementCache object. It returns an object that # can be used to query the database repeatedly. def cacheable_query(klass, arel) # :nodoc: - collected = visitor.accept(arel.ast, collector) if prepared_statements - klass.query(collected.value) + sql, binds = visitor.accept(arel.ast, collector).value + query = klass.query(sql) else - klass.partial_query(collected.value) + collector = PartialQueryCollector.new + parts, binds = visitor.accept(arel.ast, collector).value + query = klass.partial_query(parts) end + [query, binds] end # Returns an ActiveRecord::Result instance. def select_all(arel, name = nil, binds = [], preparable: nil) - arel, binds = binds_from_relation arel, binds - sql = to_sql(arel, binds) + arel = arel_from_relation(arel) + sql, binds = to_sql(arel, binds) if !prepared_statements || (arel.is_a?(String) && preparable.nil?) preparable = false else @@ -131,20 +138,23 @@ module ActiveRecord # # If the next id was calculated in advance (as in Oracle), it should be # passed in as +id_value+. - def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) - value = exec_insert(to_sql(arel, binds), name, binds, pk, sequence_name) + def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil) + sql, binds = to_sql(arel) + value = exec_insert(sql, name, binds, pk, sequence_name) id_value || last_inserted_id(value) end alias create insert # Executes the update statement and returns the number of rows affected. - def update(arel, name = nil, binds = []) - exec_update(to_sql(arel, binds), name, binds) + def update(arel, name = nil) + sql, binds = to_sql(arel) + exec_update(sql, name, binds) end # Executes the delete statement and returns the number of rows affected. - def delete(arel, name = nil, binds = []) - exec_delete(to_sql(arel, binds), name, binds) + def delete(arel, name = nil) + sql, binds = to_sql(arel) + exec_delete(sql, name, binds) end # Returns +true+ when the connection adapter supports prepared statement @@ -430,11 +440,12 @@ module ActiveRecord row && row.first end - def binds_from_relation(relation, binds) - if relation.is_a?(Relation) && binds.empty? - relation, binds = relation.arel, relation.bound_attributes + def arel_from_relation(relation) + if relation.is_a?(Relation) + relation.arel + else + relation end - [relation, binds] end # Fixture value is quoted by Arel, however scalar values @@ -447,6 +458,28 @@ module ActiveRecord value end end + + class PartialQueryCollector + def initialize + @parts = [] + @binds = [] + end + + def << str + @parts << str + self + end + + def add_bind obj + @binds << obj + @parts << Arel::Nodes::BindParam.new(1) + self + end + + def value + [@parts, @binds] + end + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 077e8beba9..ecf5201d12 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -92,8 +92,8 @@ module ActiveRecord def select_all(arel, name = nil, binds = [], preparable: nil) if @query_cache_enabled && !locked?(arel) - arel, binds = binds_from_relation arel, binds - sql = to_sql(arel, binds) + arel = arel_from_relation(arel) + sql, binds = to_sql(arel, binds) cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) } else super diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index b637518fc8..7b83bc319c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -24,6 +24,10 @@ module ActiveRecord return value.quoted_id end + if value.respond_to?(:value_for_database) + value = value.value_for_database + end + _quote(value) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 04e32d03e1..e699a77230 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -7,7 +7,9 @@ require_relative "sql_type_metadata" require_relative "abstract/schema_dumper" require_relative "abstract/schema_creation" require "arel/collectors/bind" +require "arel/collectors/composite" require "arel/collectors/sql_string" +require "arel/collectors/substitute_binds" module ActiveRecord module ConnectionAdapters # :nodoc: @@ -129,19 +131,6 @@ module ActiveRecord end end - class BindCollector < Arel::Collectors::Bind - def compile(bvs, conn) - casted_binds = bvs.map(&:value_for_database) - super(casted_binds.map { |value| conn.quote(value) }) - end - end - - class SQLString < Arel::Collectors::SQLString - def compile(bvs, conn) - super(bvs) - end - end - def valid_type?(type) # :nodoc: !native_database_types[type].nil? end @@ -432,14 +421,14 @@ module ActiveRecord end def case_sensitive_comparison(table, attribute, column, value) # :nodoc: - table[attribute].eq(value) + table[attribute].eq(Arel::Nodes::BindParam.new(value)) end def case_insensitive_comparison(table, attribute, column, value) # :nodoc: if can_perform_case_insensitive_comparison_for?(column) - table[attribute].lower.eq(table.lower(value)) + table[attribute].lower.eq(table.lower(Arel::Nodes::BindParam.new(value))) else - table[attribute].eq(value) + table[attribute].eq(Arel::Nodes::BindParam.new(value)) end end @@ -457,24 +446,6 @@ module ActiveRecord visitor.accept(node, collector).value end - def combine_bind_parameters( - from_clause: [], - join_clause: [], - where_clause: [], - having_clause: [], - limit: nil, - offset: nil - ) # :nodoc: - result = from_clause + join_clause + where_clause + having_clause - if limit - result << limit - end - if offset - result << offset - end - result - end - def default_index_type?(index) # :nodoc: index.using.nil? end @@ -609,9 +580,15 @@ module ActiveRecord def collector if prepared_statements - SQLString.new + Arel::Collectors::Composite.new( + Arel::Collectors::SQLString.new, + Arel::Collectors::Bind.new, + ) else - BindCollector.new + Arel::Collectors::SubstituteBinds.new( + self, + Arel::Collectors::SQLString.new, + ) end end 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 5915f0db2d..e762abc00f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -177,7 +177,8 @@ module ActiveRecord #++ def explain(arel, binds = []) - sql = "EXPLAIN #{to_sql(arel, binds)}" + sql, binds = to_sql(arel, binds) + sql = "EXPLAIN #{sql}" start = Time.now result = exec_query(sql, "EXPLAIN", binds) elapsed = Time.now - start @@ -494,7 +495,7 @@ module ActiveRecord def case_sensitive_comparison(table, attribute, column, value) # :nodoc: if column.collation && !column.case_sensitive? - table[attribute].eq(Arel::Nodes::Bin.new(value)) + table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new(value))) else super end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb index 00b56ee9ae..a058a72872 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb @@ -5,7 +5,7 @@ module ActiveRecord module MySQL module DatabaseStatements # Returns an ActiveRecord::Result instance. - def select_all(arel, name = nil, binds = [], preparable: nil) # :nodoc: + def select_all(*) # :nodoc: result = if ExplainRegistry.collect? && prepared_statements unprepared_statement { super } else diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 8db2a645af..0dd4aac463 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -5,7 +5,8 @@ module ActiveRecord module PostgreSQL module DatabaseStatements def explain(arel, binds = []) - sql = "EXPLAIN #{to_sql(arel, binds)}" + sql, binds = to_sql(arel, binds) + sql = "EXPLAIN #{sql}" PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds)) end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 10e80179ac..8c12cb09bd 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -203,7 +203,8 @@ module ActiveRecord #++ def explain(arel, binds = []) - sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}" + sql, binds = to_sql(arel, binds) + sql = "EXPLAIN QUERY PLAN #{sql}" SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", [])) end diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index ab2bfb63a6..5005d58f1c 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -182,7 +182,6 @@ module ActiveRecord each_counter_cached_associations do |association| if send(association.reflection.name) association.increment_counters - @_after_create_counter_called = true end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 90e4fe98d5..1c4011b75e 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -53,7 +53,7 @@ module ActiveRecord im = arel.create_insert im.into @table - substitutes, binds = substitute_values values + substitutes = substitute_values values if values.empty? # empty insert im.values = Arel.sql(connection.empty_insert_statement_value) @@ -67,11 +67,11 @@ module ActiveRecord primary_key || false, primary_key_value, nil, - binds) + ) end def _update_record(values, id, id_was) # :nodoc: - substitutes, binds = substitute_values values + substitutes = substitute_values values scope = @klass.unscoped @@ -80,7 +80,6 @@ module ActiveRecord end relation = scope.where(@klass.primary_key => (id_was || id)) - bvs = binds + relation.bound_attributes um = relation .arel .compile_update(substitutes, @klass.primary_key) @@ -88,20 +87,14 @@ module ActiveRecord @klass.connection.update( um, "SQL", - bvs, ) end def substitute_values(values) # :nodoc: - binds = [] - substitutes = [] - - values.each do |arel_attr, value| - binds.push QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name)) - substitutes.push [arel_attr, Arel::Nodes::BindParam.new] + values.map do |arel_attr, value| + bind = QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name)) + [arel_attr, Arel::Nodes::BindParam.new(bind)] end - - [substitutes, binds] end def arel_attribute(name) # :nodoc: @@ -380,7 +373,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - @klass.connection.update stmt, "SQL", bound_attributes + @klass.connection.update stmt, "SQL" end # Updates an object (or multiple objects) and saves it to the database, if validations pass. @@ -510,7 +503,7 @@ module ActiveRecord stmt.wheres = arel.constraints end - affected = @klass.connection.delete(stmt, "SQL", bound_attributes) + affected = @klass.connection.delete(stmt, "SQL") reset affected @@ -578,7 +571,8 @@ module ActiveRecord conn = klass.connection conn.unprepared_statement { - conn.to_sql(relation.arel, relation.bound_attributes) + sql, _ = conn.to_sql(relation.arel) + sql } end end @@ -648,6 +642,10 @@ module ActiveRecord @values == klass.unscoped.values end + def has_limit_or_offset? # :nodoc: + limit_value || offset_value + end + protected def load_records(records) @@ -663,7 +661,7 @@ module ActiveRecord def exec_queries(&block) skip_query_cache_if_necessary do - @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes, &block).freeze + @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, &block).freeze preload = preload_values preload += includes_values unless eager_loading? diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index c105abe774..d3b5be6bce 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -131,7 +131,7 @@ module ActiveRecord def calculate(operation, column_name) if has_include?(column_name) relation = construct_relation_for_association_calculations - relation = relation.distinct if operation.to_s.downcase == "count" + relation.distinct! if operation.to_s.downcase == "count" relation.calculate(operation, column_name) else @@ -186,7 +186,7 @@ module ActiveRecord relation.select_values = column_names.map { |cn| @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn } - result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil, bound_attributes) } + result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) } result.cast_values(klass.attribute_types) end end @@ -214,8 +214,13 @@ module ActiveRecord if operation == "count" column_name ||= select_for_count - column_name = primary_key if column_name == :all && distinct - distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i + if column_name == :all + if distinct && !(has_limit_or_offset? && order_values.any?) + column_name = primary_key + end + elsif column_name =~ /\s*DISTINCT[\s(]+/i + distinct = nil + end end if group_values.any? @@ -242,7 +247,7 @@ module ActiveRecord def execute_simple_calculation(operation, column_name, distinct) #:nodoc: column_alias = column_name - if operation == "count" && (limit_value || offset_value) + if operation == "count" && has_limit_or_offset? # Shortcut when limit is zero. return 0 if limit_value == 0 @@ -262,7 +267,7 @@ module ActiveRecord query_builder = relation.arel end - result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil, bound_attributes) } + result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) } row = result.first value = row && row.values.first type = result.column_types.fetch(column_alias) do @@ -313,7 +318,7 @@ module ActiveRecord relation.group_values = group_fields relation.select_values = select_values - calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil, relation.bound_attributes) } + calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) } if association key_ids = calculated_data.collect { |row| row[group_aliases.first] } @@ -381,14 +386,18 @@ module ActiveRecord end def build_count_subquery(relation, column_name, distinct) - column_alias = Arel.sql("count_column") - subquery_alias = Arel.sql("subquery_for_count") + relation.select_values = [ + if column_name == :all + distinct ? table[Arel.star] : Arel.sql("1") + else + column_alias = Arel.sql("count_column") + aggregate_column(column_name).as(column_alias) + end + ] - aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias) - relation.select_values = [aliased_column] - subquery = relation.arel.as(subquery_alias) + subquery = relation.arel.as(Arel.sql("subquery_for_count")) + select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false) - select_value = operation_over_aggregate_column(column_alias, "count", distinct) Arel::SelectManager.new(subquery).project(select_value) end end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 36d35b98fd..626c50470e 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -317,7 +317,7 @@ module ActiveRecord relation = construct_relation_for_exists(relation, conditions) - skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists", relation.bound_attributes) } ? true : false + skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false rescue ::RangeError false end @@ -378,7 +378,7 @@ module ActiveRecord if ActiveRecord::NullRelation === relation [] else - rows = skip_query_cache_if_necessary { connection.select_all(relation.arel, "SQL", relation.bound_attributes) } + rows = skip_query_cache_if_necessary { connection.select_all(relation.arel, "SQL") } join_dependency.instantiate(rows, aliases) end end @@ -426,7 +426,7 @@ module ActiveRecord relation = relation.except(:select).select(values).distinct! - id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL", relation.bound_attributes) } + id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") } id_rows.map { |row| row[primary_key] } end diff --git a/activerecord/lib/active_record/relation/from_clause.rb b/activerecord/lib/active_record/relation/from_clause.rb index 03f1202470..c53a682aee 100644 --- a/activerecord/lib/active_record/relation/from_clause.rb +++ b/activerecord/lib/active_record/relation/from_clause.rb @@ -10,14 +10,6 @@ module ActiveRecord @name = name end - def binds - if value.is_a?(Relation) - value.bound_attributes - else - [] - end - end - def merge(other) self end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 3b8f8da634..5c42414072 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -8,10 +8,9 @@ module ActiveRecord @table = table @handlers = [] - register_handler(BasicObject, BasicObjectHandler.new) + register_handler(BasicObject, BasicObjectHandler.new(self)) register_handler(Base, BaseHandler.new(self)) - register_handler(Range, RangeHandler.new) - register_handler(RangeHandler::RangeWithBinds, RangeHandler.new) + register_handler(Range, RangeHandler.new(self)) register_handler(Relation, RelationHandler.new) register_handler(Array, ArrayHandler.new(self)) end @@ -21,11 +20,6 @@ module ActiveRecord expand_from_hash(attributes) end - def create_binds(attributes) - attributes = convert_dot_notation_to_hash(attributes) - create_binds_for_hash(attributes) - end - def self.references(attributes) attributes.map do |key, value| if value.is_a?(Hash) @@ -56,8 +50,11 @@ module ActiveRecord handler_for(value).call(attribute, value) end - # TODO Change this to private once we've dropped Ruby 2.2 support. - # Workaround for Ruby 2.2 "private attribute?" warning. + def build_bind_attribute(column_name, value) + attr = Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name)) + Arel::Nodes::BindParam.new(attr) + end + protected attr_reader :table @@ -68,29 +65,13 @@ module ActiveRecord attributes.flat_map do |key, value| if value.is_a?(Hash) && !table.has_column?(key) associated_predicate_builder(key).expand_from_hash(value) - else - build(table.arel_attribute(key), value) - end - end - end - - def create_binds_for_hash(attributes) - result = attributes.dup - binds = [] - - attributes.each do |column_name, value| - case - when value.is_a?(Hash) && !table.has_column?(column_name) - attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value) - result[column_name] = attrs - binds += bvs - when table.associated_with?(column_name) + elsif table.associated_with?(key) # Find the foreign key when using queries such as: # Post.where(author: author) # # For polymorphic relationships, find the foreign key and type: # PriceEstimate.where(estimate_of: treasure) - associated_table = table.associated_table(column_name) + associated_table = table.associated_table(key) if associated_table.polymorphic_association? case value.is_a?(Array) ? value.first : value when Base, Relation @@ -100,40 +81,18 @@ module ActiveRecord end klass ||= AssociationQueryValue - result[column_name] = klass.new(associated_table, value).queries.map do |query| - attrs, bvs = create_binds_for_hash(query) - binds.concat(bvs) - attrs - end - when value.is_a?(Range) && !table.type(column_name).respond_to?(:subtype) - first = value.begin - last = value.end - unless first.respond_to?(:infinite?) && first.infinite? - binds << build_bind_attribute(column_name, first) - first = Arel::Nodes::BindParam.new + queries = klass.new(associated_table, value).queries.map do |query| + expand_from_hash(query).reduce(&:and) end - unless last.respond_to?(:infinite?) && last.infinite? - binds << build_bind_attribute(column_name, last) - last = Arel::Nodes::BindParam.new - end - - result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?) - when value.is_a?(Relation) - binds.concat(value.bound_attributes) + queries.reduce(&:or) + # FIXME: Deprecate this and provide a public API to force equality + elsif (value.is_a?(Range) || value.is_a?(Array)) && + table.type(key.to_s).respond_to?(:subtype) + BasicObjectHandler.new(self).call(table.arel_attribute(key), value) else - if can_be_bound?(column_name, value) - bind_attribute = build_bind_attribute(column_name, value) - if value.is_a?(StatementCache::Substitute) || !bind_attribute.value_for_database.nil? - result[column_name] = Arel::Nodes::BindParam.new - binds << bind_attribute - else - result[column_name] = nil - end - end + build(table.arel_attribute(key), value) end end - - [result, binds] end private @@ -161,19 +120,6 @@ module ActiveRecord def handler_for(object) @handlers.detect { |klass, _| klass === object }.last end - - def can_be_bound?(column_name, value) - case value - when Array, Range - table.type(column_name).respond_to?(:subtype) - else - !value.nil? && handler_for(value).is_a?(BasicObjectHandler) - end - end - - def build_bind_attribute(column_name, value) - Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name)) - end end end diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb index 85ec7d1428..ad617365fc 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb @@ -19,7 +19,11 @@ module ActiveRecord case values.length when 0 then NullPredicate when 1 then predicate_builder.build(attribute, values.first) - else attribute.in(values) + else + bind_values = values.map do |v| + predicate_builder.build_bind_attribute(attribute.name, v) + end + attribute.in(bind_values) end unless nils.empty? @@ -31,8 +35,6 @@ module ActiveRecord array_predicates.inject(&:or) end - # TODO Change this to private once we've dropped Ruby 2.2 support. - # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicate_builder diff --git a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb index 632c68a103..112821135f 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb @@ -11,8 +11,6 @@ module ActiveRecord predicate_builder.build(attribute, value.id) end - # TODO Change this to private once we've dropped Ruby 2.2 support. - # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicate_builder diff --git a/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb index a17cf30ffd..34db266f05 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb @@ -3,9 +3,18 @@ module ActiveRecord class PredicateBuilder class BasicObjectHandler # :nodoc: + def initialize(predicate_builder) + @predicate_builder = predicate_builder + end + def call(attribute, value) - attribute.eq(value) + bind = predicate_builder.build_bind_attribute(attribute.name, value) + attribute.eq(bind) end + + protected + + attr_reader :predicate_builder end end end diff --git a/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb index 072e238306..6d16579708 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb @@ -3,25 +3,39 @@ module ActiveRecord class PredicateBuilder class RangeHandler # :nodoc: - RangeWithBinds = Struct.new(:begin, :end, :exclude_end?) + class RangeWithBinds < Struct.new(:begin, :end) + def exclude_end? + false + end + end + + def initialize(predicate_builder) + @predicate_builder = predicate_builder + end def call(attribute, value) + begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin) + end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end) if value.begin.respond_to?(:infinite?) && value.begin.infinite? if value.end.respond_to?(:infinite?) && value.end.infinite? attribute.not_in([]) elsif value.exclude_end? - attribute.lt(value.end) + attribute.lt(end_bind) else - attribute.lteq(value.end) + attribute.lteq(end_bind) end elsif value.end.respond_to?(:infinite?) && value.end.infinite? - attribute.gteq(value.begin) + attribute.gteq(begin_bind) elsif value.exclude_end? - attribute.gteq(value.begin).and(attribute.lt(value.end)) + attribute.gteq(begin_bind).and(attribute.lt(end_bind)) else - attribute.between(value) + attribute.between(RangeWithBinds.new(begin_bind, end_bind)) end end + + protected + + attr_reader :predicate_builder end end end diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb index e6883acd90..5a9a7fd432 100644 --- a/activerecord/lib/active_record/relation/query_attribute.rb +++ b/activerecord/lib/active_record/relation/query_attribute.rb @@ -16,6 +16,11 @@ module ActiveRecord def with_cast_value(value) QueryAttribute.new(name, value, type) end + + def nil? + !value_before_type_cast.is_a?(StatementCache::Substitute) && + (value_before_type_cast.nil? || value_for_database.nil?) + end end end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e2af62873c..812c8e7e3f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -76,31 +76,6 @@ module ActiveRecord CODE end - def bound_attributes - if limit_value - limit_bind = Attribute.with_cast_value( - "LIMIT".freeze, - connection.sanitize_limit(limit_value), - Type.default_value, - ) - end - if offset_value - offset_bind = Attribute.with_cast_value( - "OFFSET".freeze, - offset_value.to_i, - Type.default_value, - ) - end - connection.combine_bind_parameters( - from_clause: from_clause.binds, - join_clause: arel.bind_values, - where_clause: where_clause.binds, - having_clause: having_clause.binds, - limit: limit_bind, - offset: offset_bind, - ) - end - alias extensions extending_values # Specify relationships to be included in the result set. For @@ -952,8 +927,22 @@ module ActiveRecord arel.where(where_clause.ast) unless where_clause.empty? arel.having(having_clause.ast) unless having_clause.empty? - arel.take(Arel::Nodes::BindParam.new) if limit_value - arel.skip(Arel::Nodes::BindParam.new) if offset_value + if limit_value + limit_attribute = Attribute.with_cast_value( + "LIMIT".freeze, + connection.sanitize_limit(limit_value), + Type.default_value, + ) + arel.take(Arel::Nodes::BindParam.new(limit_attribute)) + end + if offset_value + offset_attribute = Attribute.with_cast_value( + "OFFSET".freeze, + offset_value.to_i, + Type.default_value, + ) + arel.skip(Arel::Nodes::BindParam.new(offset_attribute)) + end arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty? build_order(arel) @@ -1029,7 +1018,6 @@ module ActiveRecord join_infos.each do |info| info.joins.each { |join| manager.from(join) } - manager.bind_values.concat info.binds end manager.join_sources.concat(join_list) diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb index 0b4c7b7dfa..ef2bca9155 100644 --- a/activerecord/lib/active_record/relation/where_clause.rb +++ b/activerecord/lib/active_record/relation/where_clause.rb @@ -3,31 +3,26 @@ module ActiveRecord class Relation class WhereClause # :nodoc: - attr_reader :binds - delegate :any?, :empty?, to: :predicates - def initialize(predicates, binds) + def initialize(predicates) @predicates = predicates - @binds = binds end def +(other) WhereClause.new( predicates + other.predicates, - binds + other.binds, ) end def merge(other) WhereClause.new( predicates_unreferenced_by(other) + other.predicates, - non_conflicting_binds(other) + other.binds, ) end def except(*columns) - WhereClause.new(*except_predicates_and_binds(columns)) + WhereClause.new(except_predicates(columns)) end def or(other) @@ -38,7 +33,6 @@ module ActiveRecord else WhereClause.new( [ast.or(other.ast)], - binds + other.binds ) end end @@ -51,17 +45,10 @@ module ActiveRecord end end - binds = self.binds.map { |attr| [attr.name, attr.value] }.to_h - equalities.map { |node| name = node.left.name.to_s - [name, binds.fetch(name) { - case node.right - when Array then node.right.map(&:val) - when Arel::Nodes::Casted, Arel::Nodes::Quoted - node.right.val - end - }] + value = extract_node_value(node.right) + [name, value] }.to_h end @@ -71,20 +58,17 @@ module ActiveRecord def ==(other) other.is_a?(WhereClause) && - predicates == other.predicates && - binds == other.binds + predicates == other.predicates end def invert - WhereClause.new(inverted_predicates, binds) + WhereClause.new(inverted_predicates) end def self.empty - @empty ||= new([], []) + @empty ||= new([]) end - # TODO Change this to private once we've dropped Ruby 2.2 support. - # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :predicates @@ -108,12 +92,6 @@ module ActiveRecord node.respond_to?(:operator) && node.operator == :== end - def non_conflicting_binds(other) - conflicts = referenced_columns & other.referenced_columns - conflicts.map! { |node| node.name.to_s } - binds.reject { |attr| conflicts.include?(attr.name) } - end - def inverted_predicates predicates.map { |node| invert_predicate(node) } end @@ -133,44 +111,22 @@ module ActiveRecord end end - def except_predicates_and_binds(columns) - except_binds = [] - binds_index = 0 - - predicates = self.predicates.reject do |node| - binds_contains = node.grep(Arel::Nodes::BindParam).size if node.is_a?(Arel::Nodes::Node) - - except = \ - case node - when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual - subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right) - columns.include?(subrelation.name.to_s) - end - - if except && binds_contains > 0 - (binds_index...(binds_index + binds_contains)).each do |i| - except_binds[i] = true - end + def except_predicates(columns) + self.predicates.reject do |node| + case node + when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual + subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right) + columns.include?(subrelation.name.to_s) end - - binds_index += binds_contains if binds_contains - - except end - - binds = self.binds.reject.with_index do |_, i| - except_binds[i] - end - - [predicates, binds] end def predicates_with_wrapped_sql_literals non_empty_predicates.map do |node| - if Arel::Nodes::Equality === node - node - else + case node + when Arel::Nodes::SqlLiteral, ::String wrap_sql_literal(node) + else node end end end @@ -186,6 +142,22 @@ module ActiveRecord end Arel::Nodes::Grouping.new(node) end + + def extract_node_value(node) + case node + when Array + node.map { |v| extract_node_value(v) } + when Arel::Nodes::Casted, Arel::Nodes::Quoted + node.val + when Arel::Nodes::BindParam + value = node.value + if value.respond_to?(:value_before_type_cast) + value.value_before_type_cast + else + value + end + end + end end end end diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb index 4a0868314d..1374785354 100644 --- a/activerecord/lib/active_record/relation/where_clause_factory.rb +++ b/activerecord/lib/active_record/relation/where_clause_factory.rb @@ -17,63 +17,19 @@ module ActiveRecord attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes) attributes.stringify_keys! - if perform_case_sensitive?(options = other.last) - parts, binds = build_for_case_sensitive(attributes, options) - else - attributes, binds = predicate_builder.create_binds(attributes) - parts = predicate_builder.build_from_hash(attributes) - end + parts = predicate_builder.build_from_hash(attributes) when Arel::Nodes::Node parts = [opts] else raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})" end - WhereClause.new(parts, binds || []) + WhereClause.new(parts) end - # TODO Change this to private once we've dropped Ruby 2.2 support. - # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :klass, :predicate_builder - - private - - def perform_case_sensitive?(options) - options && options.key?(:case_sensitive) - end - - def build_for_case_sensitive(attributes, options) - parts, binds = [], [] - table = klass.arel_table - - attributes.each do |attribute, value| - if reflection = klass._reflect_on_association(attribute) - attribute = reflection.foreign_key.to_s - value = value[reflection.klass.primary_key] unless value.nil? - end - - if value.nil? - parts << table[attribute].eq(value) - else - column = klass.column_for_attribute(attribute) - - binds << predicate_builder.send(:build_bind_attribute, attribute, value) - value = Arel::Nodes::BindParam.new - - predicate = if options[:case_sensitive] - klass.connection.case_sensitive_comparison(table, attribute, column, value) - else - klass.connection.case_insensitive_comparison(table, attribute, column, value) - end - - parts << predicate - end - end - - [parts, binds] - end end end end diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb index b03ff9bc6c..64657089b5 100644 --- a/activerecord/lib/active_record/statement_cache.rb +++ b/activerecord/lib/active_record/statement_cache.rb @@ -90,9 +90,9 @@ module ActiveRecord attr_reader :bind_map, :query_builder def self.create(connection, block = Proc.new) - relation = block.call Params.new - bind_map = BindMap.new relation.bound_attributes - query_builder = connection.cacheable_query(self, relation.arel) + relation = block.call Params.new + query_builder, binds = connection.cacheable_query(self, relation.arel) + bind_map = BindMap.new(binds) new query_builder, bind_map end diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 5c6a796857..0f3f84ca08 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -261,7 +261,7 @@ module ActiveRecord def check_schema_file(filename) unless File.exist?(filename) - message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.} + message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}.dup message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails.root) Kernel.abort message end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 3a110b461d..4f5b157af9 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -52,7 +52,37 @@ module ActiveRecord end def build_relation(klass, attribute, value) - klass.unscoped.where!({ attribute => value }, options) + if reflection = klass._reflect_on_association(attribute) + attribute = reflection.foreign_key + value = value.attributes[reflection.klass.primary_key] unless value.nil? + end + + if value.nil? + return klass.unscoped.where!(attribute => value) + end + + # the attribute may be an aliased attribute + if klass.attribute_alias?(attribute) + attribute = klass.attribute_alias(attribute) + end + + attribute_name = attribute.to_s + + table = klass.arel_table + column = klass.columns_hash[attribute_name] + cast_type = klass.type_for_attribute(attribute_name) + + value = Relation::QueryAttribute.new(attribute_name, value, cast_type) + comparison = if !options[:case_sensitive] + # will use SQL LOWER function before comparison, unless it detects a case insensitive collation + klass.connection.case_insensitive_comparison(table, attribute, column, value) + else + klass.connection.case_sensitive_comparison(table, attribute, column, value) + end + klass.unscoped.tap do |scope| + parts = [comparison] + scope.where_clause += Relation::WhereClause.new(parts) + end end def scope_relation(record, relation) diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index e18f29bdef..abb7a696ce 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -244,7 +244,7 @@ module ActiveRecord def test_select_all_with_legacy_binds post = Post.create!(title: "foo", body: "bar") expected = @connection.select_all("SELECT * FROM posts WHERE id = #{post.id}") - result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new.to_sql}", nil, [[nil, post.id]]) + result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new(nil).to_sql}", nil, [[nil, post.id]]) assert_equal expected.to_hash, result.to_hash end end @@ -253,7 +253,6 @@ module ActiveRecord author = Author.create!(name: "john") Post.create!(author: author, title: "foo", body: "bar") query = author.posts.where(title: "foo").select(:title) - assert_equal({ "title" => "foo" }, @connection.select_one(query.arel, nil, query.bound_attributes)) assert_equal({ "title" => "foo" }, @connection.select_one(query)) assert @connection.select_all(query).is_a?(ActiveRecord::Result) assert_equal "foo", @connection.select_value(query) @@ -263,7 +262,6 @@ module ActiveRecord def test_select_methods_passing_a_relation Post.create!(title: "foo", body: "bar") query = Post.where(title: "foo").select(:title) - assert_equal({ "title" => "foo" }, @connection.select_one(query.arel, nil, query.bound_attributes)) assert_equal({ "title" => "foo" }, @connection.select_one(query)) assert @connection.select_all(query).is_a?(ActiveRecord::Result) assert_equal "foo", @connection.select_value(query) diff --git a/activerecord/test/cases/associations/association_scope_test.rb b/activerecord/test/cases/associations/association_scope_test.rb deleted file mode 100644 index 00e989153f..0000000000 --- a/activerecord/test/cases/associations/association_scope_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require "cases/helper" -require "models/post" -require "models/author" - -module ActiveRecord - module Associations - class AssociationScopeTest < ActiveRecord::TestCase - test "does not duplicate conditions" do - scope = AssociationScope.scope(Author.new.association(:welcome_posts)) - binds = scope.where_clause.binds.map(&:value) - assert_equal binds.uniq, binds - end - end - end -end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 7e4ded9fc9..0f7a249bf3 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -1171,6 +1171,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase Column.create! record: record assert_equal 1, Column.count end + + def test_multiple_counter_cache_with_after_create_update + post = posts(:welcome) + parent = comments(:greetings) + + assert_difference "parent.reload.children_count", +1 do + assert_difference "post.reload.comments_count", +1 do + CommentWithAfterCreateUpdate.create(body: "foo", post: post, parent: parent) + end + end + end end class BelongsToWithForeignKeyTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 5dbb7f788a..1d33564989 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1054,29 +1054,15 @@ class BasicsTest < ActiveRecord::TestCase def test_count_with_join res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'" - res2 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count assert_equal res, res2 - res3 = nil - assert_nothing_raised do - res3 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count - end - assert_equal res, res3 - res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id" - res5 = nil - assert_nothing_raised do - res5 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").count - end - + res5 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").count assert_equal res4, res5 res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id" - res7 = nil - assert_nothing_raised do - res7 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").distinct.count - end + res7 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").distinct.count assert_equal res6, res7 end diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb index 7ac3b5b197..91cc49385c 100644 --- a/activerecord/test/cases/bind_parameter_test.rb +++ b/activerecord/test/cases/bind_parameter_test.rb @@ -41,7 +41,7 @@ if ActiveRecord::Base.connection.prepared_statements end def test_binds_are_logged - sub = Arel::Nodes::BindParam.new + sub = Arel::Nodes::BindParam.new(1) binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)] sql = "select * from topics where id = #{sub.to_sql}" diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index dd6815b149..92d071187d 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -236,6 +236,30 @@ class CalculationsTest < ActiveRecord::TestCase end end + def test_distinct_count_with_order_and_limit + assert_equal 4, Account.distinct.order(:firm_id).limit(4).count + end + + def test_distinct_count_with_order_and_offset + assert_equal 4, Account.distinct.order(:firm_id).offset(2).count + end + + def test_distinct_count_with_order_and_limit_and_offset + assert_equal 4, Account.distinct.order(:firm_id).limit(4).offset(2).count + end + + def test_distinct_joins_count_with_order_and_limit + assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).limit(3).count + end + + def test_distinct_joins_count_with_order_and_offset + assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).offset(2).count + end + + def test_distinct_joins_count_with_order_and_limit_and_offset + assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).limit(3).offset(2).count + end + def test_should_group_by_summed_field_having_condition c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit) assert_nil c[1] diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 239bf32068..a263106f6d 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -420,7 +420,7 @@ class InheritanceTest < ActiveRecord::TestCase def test_eager_load_belongs_to_primary_key_quoting con = Account.connection - bind_param = Arel::Nodes::BindParam.new + bind_param = Arel::Nodes::BindParam.new(nil) assert_sql(/#{con.quote_table_name('companies')}\.#{con.quote_column_name('id')} = (?:#{Regexp.quote(bind_param.to_sql)}|1)/) do Account.all.merge!(includes: :firm).find(1) end diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 44e2b16923..b68b3723f6 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -81,13 +81,11 @@ class RelationMergingTest < ActiveRecord::TestCase end test "merge collapses wheres from the LHS only" do - left = Post.where(title: "omg").where(comments_count: 1) + left = Post.where(title: "omg").where(comments_count: 1) right = Post.where(title: "wtf").where(title: "bbq") - expected = [left.bound_attributes[1]] + right.bound_attributes - merged = left.merge(right) + merged = left.merge(right) - assert_equal expected, merged.bound_attributes assert_not_includes merged.to_sql, "omg" assert_includes merged.to_sql, "wtf" assert_includes merged.to_sql, "bbq" diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb index 42c6718220..a68eb2b446 100644 --- a/activerecord/test/cases/relation/where_chain_test.rb +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -27,7 +27,7 @@ module ActiveRecord end def test_association_not_eq - expected = Arel::Nodes::Grouping.new(Comment.arel_table[@name].not_eq(Arel::Nodes::BindParam.new)) + expected = Comment.arel_table[@name].not_eq(Arel::Nodes::BindParam.new(1)) relation = Post.joins(:comments).where.not(comments: { title: "hello" }) assert_equal(expected.to_sql, relation.where_clause.ast.to_sql) end diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb index d43f32e1af..f3a81f3c70 100644 --- a/activerecord/test/cases/relation/where_clause_test.rb +++ b/activerecord/test/cases/relation/where_clause_test.rb @@ -5,76 +5,82 @@ require "cases/helper" class ActiveRecord::Relation class WhereClauseTest < ActiveRecord::TestCase test "+ combines two where clauses" do - first_clause = WhereClause.new([table["id"].eq(bind_param)], [["id", 1]]) - second_clause = WhereClause.new([table["name"].eq(bind_param)], [["name", "Sean"]]) + first_clause = WhereClause.new([table["id"].eq(bind_param(1))]) + second_clause = WhereClause.new([table["name"].eq(bind_param("Sean"))]) combined = WhereClause.new( - [table["id"].eq(bind_param), table["name"].eq(bind_param)], - [["id", 1], ["name", "Sean"]], + [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean"))], ) assert_equal combined, first_clause + second_clause end test "+ is associative, but not commutative" do - a = WhereClause.new(["a"], ["bind a"]) - b = WhereClause.new(["b"], ["bind b"]) - c = WhereClause.new(["c"], ["bind c"]) + a = WhereClause.new(["a"]) + b = WhereClause.new(["b"]) + c = WhereClause.new(["c"]) assert_equal a + (b + c), (a + b) + c assert_not_equal a + b, b + a end test "an empty where clause is the identity value for +" do - clause = WhereClause.new([table["id"].eq(bind_param)], [["id", 1]]) + clause = WhereClause.new([table["id"].eq(bind_param(1))]) assert_equal clause, clause + WhereClause.empty end test "merge combines two where clauses" do - a = WhereClause.new([table["id"].eq(1)], []) - b = WhereClause.new([table["name"].eq("Sean")], []) - expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")], []) + a = WhereClause.new([table["id"].eq(1)]) + b = WhereClause.new([table["name"].eq("Sean")]) + expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")]) assert_equal expected, a.merge(b) end test "merge keeps the right side, when two equality clauses reference the same column" do - a = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")], []) - b = WhereClause.new([table["name"].eq("Jim")], []) - expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Jim")], []) + a = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")]) + b = WhereClause.new([table["name"].eq("Jim")]) + expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Jim")]) assert_equal expected, a.merge(b) end test "merge removes bind parameters matching overlapping equality clauses" do a = WhereClause.new( - [table["id"].eq(bind_param), table["name"].eq(bind_param)], - [attribute("id", 1), attribute("name", "Sean")], + [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean"))], ) b = WhereClause.new( - [table["name"].eq(bind_param)], - [attribute("name", "Jim")] + [table["name"].eq(bind_param("Jim"))], ) expected = WhereClause.new( - [table["id"].eq(bind_param), table["name"].eq(bind_param)], - [attribute("id", 1), attribute("name", "Jim")], + [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Jim"))], ) assert_equal expected, a.merge(b) end test "merge allows for columns with the same name from different tables" do - skip "This is not possible as of 4.2, and the binds do not yet contain sufficient information for this to happen" - # We might be able to change the implementation to remove conflicts by index, rather than column name + table2 = Arel::Table.new("table2") + a = WhereClause.new( + [table["id"].eq(bind_param(1)), table2["id"].eq(bind_param(2))], + ) + b = WhereClause.new( + [table["id"].eq(bind_param(3))], + ) + expected = WhereClause.new( + [table2["id"].eq(bind_param(2)), table["id"].eq(bind_param(3))], + ) + + assert_equal expected, a.merge(b) end test "a clause knows if it is empty" do assert WhereClause.empty.empty? - assert_not WhereClause.new(["anything"], []).empty? + assert_not WhereClause.new(["anything"]).empty? end test "invert cannot handle nil" do - where_clause = WhereClause.new([nil], []) + where_clause = WhereClause.new([nil]) assert_raises ArgumentError do where_clause.invert @@ -88,13 +94,13 @@ class ActiveRecord::Relation table["id"].eq(1), "sql literal", random_object - ], []) + ]) expected = WhereClause.new([ table["id"].not_in([1, 2, 3]), table["id"].not_eq(1), Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new("sql literal")), Arel::Nodes::Not.new(random_object) - ], []) + ]) assert_equal expected, original.invert end @@ -102,20 +108,17 @@ class ActiveRecord::Relation test "except removes binary predicates referencing a given column" do where_clause = WhereClause.new([ table["id"].in([1, 2, 3]), - table["name"].eq(bind_param), - table["age"].gteq(bind_param), - ], [ - attribute("name", "Sean"), - attribute("age", 30), + table["name"].eq(bind_param("Sean")), + table["age"].gteq(bind_param(30)), ]) - expected = WhereClause.new([table["age"].gteq(bind_param)], [attribute("age", 30)]) + expected = WhereClause.new([table["age"].gteq(bind_param(30))]) assert_equal expected, where_clause.except("id", "name") end test "except jumps over unhandled binds (like with OR) correctly" do wcs = (0..9).map do |i| - WhereClause.new([table["id#{i}"].eq(bind_param)], [attribute("id#{i}", i)]) + WhereClause.new([table["id#{i}"].eq(bind_param(i))]) end wc = wcs[0] + wcs[1] + wcs[2].or(wcs[3]) + wcs[4] + wcs[5] + wcs[6].or(wcs[7]) + wcs[8] + wcs[9] @@ -123,18 +126,15 @@ class ActiveRecord::Relation expected = wcs[0] + wcs[2].or(wcs[3]) + wcs[5] + wcs[6].or(wcs[7]) + wcs[9] actual = wc.except("id1", "id2", "id4", "id7", "id8") - # Easier to read than the inspect of where_clause - assert_equal expected.ast.to_sql, actual.ast.to_sql - assert_equal expected.binds.map(&:value), actual.binds.map(&:value) assert_equal expected, actual end test "ast groups its predicates with AND" do predicates = [ table["id"].in([1, 2, 3]), - table["name"].eq(bind_param), + table["name"].eq(bind_param(nil)), ] - where_clause = WhereClause.new(predicates, []) + where_clause = WhereClause.new(predicates) expected = Arel::Nodes::And.new(predicates) assert_equal expected, where_clause.ast @@ -146,38 +146,36 @@ class ActiveRecord::Relation table["id"].in([1, 2, 3]), "foo = bar", random_object, - ], []) + ]) expected = Arel::Nodes::And.new([ table["id"].in([1, 2, 3]), Arel::Nodes::Grouping.new(Arel.sql("foo = bar")), - Arel::Nodes::Grouping.new(random_object), + random_object, ]) assert_equal expected, where_clause.ast end test "ast removes any empty strings" do - where_clause = WhereClause.new([table["id"].in([1, 2, 3])], []) - where_clause_with_empty = WhereClause.new([table["id"].in([1, 2, 3]), ""], []) + where_clause = WhereClause.new([table["id"].in([1, 2, 3])]) + where_clause_with_empty = WhereClause.new([table["id"].in([1, 2, 3]), ""]) assert_equal where_clause.ast, where_clause_with_empty.ast end test "or joins the two clauses using OR" do - where_clause = WhereClause.new([table["id"].eq(bind_param)], [attribute("id", 1)]) - other_clause = WhereClause.new([table["name"].eq(bind_param)], [attribute("name", "Sean")]) + where_clause = WhereClause.new([table["id"].eq(bind_param(1))]) + other_clause = WhereClause.new([table["name"].eq(bind_param("Sean"))]) expected_ast = Arel::Nodes::Grouping.new( - Arel::Nodes::Or.new(table["id"].eq(bind_param), table["name"].eq(bind_param)) + Arel::Nodes::Or.new(table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean"))) ) - expected_binds = where_clause.binds + other_clause.binds assert_equal expected_ast.to_sql, where_clause.or(other_clause).ast.to_sql - assert_equal expected_binds, where_clause.or(other_clause).binds end test "or returns an empty where clause when either side is empty" do - where_clause = WhereClause.new([table["id"].eq(bind_param)], [attribute("id", 1)]) + where_clause = WhereClause.new([table["id"].eq(bind_param(1))]) assert_equal WhereClause.empty, where_clause.or(WhereClause.empty) assert_equal WhereClause.empty, WhereClause.empty.or(where_clause) @@ -189,12 +187,8 @@ class ActiveRecord::Relation Arel::Table.new("table") end - def bind_param - Arel::Nodes::BindParam.new - end - - def attribute(name, value) - ActiveRecord::Attribute.with_cast_value(name, value, ActiveRecord::Type::Value.new) + def bind_param(value) + Arel::Nodes::BindParam.new(value) end end end diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 977165cf2e..c45fd38bc9 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -127,7 +127,7 @@ module ActiveRecord car = cars(:honda) expected = [price_estimates(:diamond), price_estimates(:sapphire_1), price_estimates(:sapphire_2), price_estimates(:honda)].sort - actual = PriceEstimate.where(estimate_of: [treasure_1, treasure_2, car]).to_a.sort + actual = PriceEstimate.where(estimate_of: [treasure_1, treasure_2, car]).to_a.sort assert_equal expected, actual end diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index b1e14f8dc4..f22fcd7b5a 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -185,7 +185,7 @@ module ActiveRecord relation = Relation.new(klass, :b, nil) relation.merge!(where: ["foo = ?", "bar"]) - assert_equal Relation::WhereClause.new(["foo = bar"], []), relation.where_clause + assert_equal Relation::WhereClause.new(["foo = bar"]), relation.where_clause end def test_merging_readonly_false diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index a9154294ad..ae1dc35bff 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1761,7 +1761,7 @@ class RelationTest < ActiveRecord::TestCase test "relations with cached arel can't be mutated [internal API]" do relation = Post.all - relation.count + relation.arel assert_raises(ActiveRecord::ImmutableRelation) { relation.limit!(5) } assert_raises(ActiveRecord::ImmutableRelation) { relation.where!("1 = 2") } @@ -1860,33 +1860,6 @@ class RelationTest < ActiveRecord::TestCase assert_equal 1, posts.unscope(where: :body).count end - def test_unscope_removes_binds - left = Post.where(id: 20) - - assert_equal 1, left.bound_attributes.length - - relation = left.unscope(where: :id) - assert_equal [], relation.bound_attributes - end - - def test_merging_removes_rhs_binds - left = Post.where(id: 20) - right = Post.where(id: [1, 2, 3, 4]) - - assert_equal 1, left.bound_attributes.length - - merged = left.merge(right) - assert_equal [], merged.bound_attributes - end - - def test_merging_keeps_lhs_binds - right = Post.where(id: 20) - left = Post.where(id: 10) - - merged = left.merge(right) - assert_equal [20], merged.bound_attributes.map(&:value) - end - def test_locked_should_not_build_arel posts = Post.locked assert posts.locked? @@ -1897,24 +1870,6 @@ class RelationTest < ActiveRecord::TestCase assert_equal "Thank you for the welcome,Thank you again for the welcome", Post.first.comments.join(",") end - def test_connection_adapters_can_reorder_binds - posts = Post.limit(1).offset(2) - - stubbed_connection = Post.connection.dup - def stubbed_connection.combine_bind_parameters(**kwargs) - offset = kwargs[:offset] - kwargs[:offset] = kwargs[:limit] - kwargs[:limit] = offset - super(**kwargs) - end - - posts.define_singleton_method(:connection) do - stubbed_connection - end - - assert_equal 2, posts.to_a.length - end - test "#skip_query_cache!" do Post.cache do assert_queries(1) do diff --git a/activerecord/test/models/account.rb b/activerecord/test/models/account.rb new file mode 100644 index 0000000000..0c3cd45a81 --- /dev/null +++ b/activerecord/test/models/account.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class Account < ActiveRecord::Base + belongs_to :firm, class_name: "Company" + belongs_to :unautosaved_firm, foreign_key: "firm_id", class_name: "Firm", autosave: false + + alias_attribute :available_credit, :credit_limit + + def self.destroyed_account_ids + @destroyed_account_ids ||= Hash.new { |h, k| h[k] = [] } + end + + # Test private kernel method through collection proxy using has_many. + def self.open + where("firm_name = ?", "37signals") + end + + before_destroy do |account| + if account.firm + Account.destroyed_account_ids[account.firm.id] << account.id + end + end + + validate :check_empty_credit_limit + + private + def check_empty_credit_limit + errors.add("credit_limit", :blank) if credit_limit.blank? + end + + def private_method + "Sir, yes sir!" + end +end diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index e1e2a5e0e9..61c54a77a7 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -77,3 +77,9 @@ class CommentWithDefaultScopeReferencesAssociation < Comment default_scope -> { includes(:developer).order("developers.name").references(:developer) } belongs_to :developer end + +class CommentWithAfterCreateUpdate < Comment + after_create do + update_attributes(body: "bar") + end +end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 50e9553bfe..bbc5fc2b2d 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -187,37 +187,4 @@ end class VerySpecialClient < SpecialClient end -class Account < ActiveRecord::Base - belongs_to :firm, class_name: "Company" - belongs_to :unautosaved_firm, foreign_key: "firm_id", class_name: "Firm", autosave: false - - alias_attribute :available_credit, :credit_limit - - def self.destroyed_account_ids - @destroyed_account_ids ||= Hash.new { |h, k| h[k] = [] } - end - - # Test private kernel method through collection proxy using has_many. - def self.open - where("firm_name = ?", "37signals") - end - - before_destroy do |account| - if account.firm - Account.destroyed_account_ids[account.firm.id] << account.id - end - true - end - - validate :check_empty_credit_limit - - private - - def check_empty_credit_limit - errors.add("credit_limit", :blank) if credit_limit.blank? - end - - def private_method - "Sir, yes sir!" - end -end +require "models/account" diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index f5542c6d25..457eb84a62 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,25 @@ +* Add purpose and expiry support to `ActiveSupport::MessageVerifier` & + `ActiveSupport::MessageEncryptor`. + + For instance, to ensure a message is only usable for one intended purpose: + + token = @verifier.generate("x", purpose: :shipping) + + @verifier.verified(token, purpose: :shipping) # => "x" + @verifier.verified(token) # => nil + + Or make it expire after a set time: + + @verifier.generate("x", expires_in: 1.month) + @verifier.generate("y", expires_at: Time.now.end_of_year) + + Showcased with `ActiveSupport::MessageVerifier`, but works the same for + `ActiveSupport::MessageEncryptor`'s `encrypt_and_sign` and `decrypt_and_verify`. + + Pull requests: #29599, #29854 + + *Assain Jaleel* + * Make the order of `Hash#reverse_merge!` consistent with `HashWithIndifferentAccess`. *Erol Fornoles* diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 9ceb3a3a7f..090d51933a 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -22,6 +22,38 @@ module ActiveSupport # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...> # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" + # + # === Confining messages to a specific purpose + # + # By default any message can be used throughout your app. But they can also be + # confined to a specific +:purpose+. + # + # token = crypt.encrypt_and_sign("this is the chair", purpose: :login) + # + # Then that same purpose must be passed when verifying to get the data back out: + # + # crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair" + # crypt.decrypt_and_verify(token, purpose: :shipping) # => nil + # crypt.decrypt_and_verify(token) # => nil + # + # Likewise, if a message has no purpose it won't be returned when verifying with + # a specific purpose. + # + # token = crypt.encrypt_and_sign("the conversation is lively") + # crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil + # crypt.decrypt_and_verify(token) # => "the conversation is lively" + # + # === Making messages expire + # + # By default messages last forever and verifying one year from now will still + # return the original value. But messages can be set to expire at a given + # time with +:expires_in+ or +:expires_at+. + # + # crypt.encrypt_and_sign(parcel, expires_in: 1.month) + # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year) + # + # Then the messages can be verified and returned upto the expire time. + # Thereafter, verifying returns +nil+. class MessageEncryptor class << self attr_accessor :use_authenticated_message_encryption #:nodoc: diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 76b3865bf2..fdd2185f7f 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -33,6 +33,46 @@ module ActiveSupport # `:digest` key as an option while initializing the verifier: # # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256') + # + # === Confining messages to a specific purpose + # + # By default any message can be used throughout your app. But they can also be + # confined to a specific +:purpose+. + # + # token = @verifier.generate("this is the chair", purpose: :login) + # + # Then that same purpose must be passed when verifying to get the data back out: + # + # @verifier.verified(token, purpose: :login) # => "this is the chair" + # @verifier.verified(token, purpose: :shipping) # => nil + # @verifier.verified(token) # => nil + # + # @verifier.verify(token, purpose: :login) # => "this is the chair" + # @verifier.verify(token, purpose: :shipping) # => ActiveSupport::MessageVerifier::InvalidSignature + # @verifier.verify(token) # => ActiveSupport::MessageVerifier::InvalidSignature + # + # Likewise, if a message has no purpose it won't be returned when verifying with + # a specific purpose. + # + # token = @verifier.generate("the conversation is lively") + # @verifier.verified(token, purpose: :scare_tactics) # => nil + # @verifier.verified(token) # => "the conversation is lively" + # + # @verifier.verify(token, purpose: :scare_tactics) # => ActiveSupport::MessageVerifier::InvalidSignature + # @verifier.verify(token) # => "the conversation is lively" + # + # === Making messages expire + # + # By default messages last forever and verifying one year from now will still + # return the original value. But messages can be set to expire at a given + # time with +:expires_in+ or +:expires_at+. + # + # @verifier.generate(parcel, expires_in: 1.month) + # @verifier.generate(doowad, expires_at: Time.now.end_of_year) + # + # Then the messages can be verified and returned upto the expire time. + # Thereafter, the +verified+ method returns +nil+ while +verify+ raises + # <tt>ActiveSupport::MessageVerifier::InvalidSignature</tt>. class MessageVerifier class InvalidSignature < StandardError; end diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index d4a8ce762a..f626ab745f 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -19,7 +19,7 @@ class MessageVerifierTest < ActiveSupport::TestCase def setup @verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!") - @data = { some: "data", now: Time.local(2010) } + @data = { some: "data", now: Time.utc(2010) } end def test_valid_message @@ -99,6 +99,21 @@ class MessageVerifierMetadataTest < ActiveSupport::TestCase @verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!", verifier_options) end + def test_verify_raises_when_purpose_differs + assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do + @verifier.verify(@verifier.generate(@message, purpose: "payment"), purpose: "shipping") + end + end + + def test_verify_raises_when_expired + signed_message = @verifier.generate(@message, expires_in: 1.month) + + travel 2.months + assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do + @verifier.verify(signed_message) + end + end + private def generate(message, **options) @verifier.generate(message, options) diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index b607a63176..ce258341f6 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -64,9 +64,9 @@ module Rails end def print_boot_information - url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" + url = "on #{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma? puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" - puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}" + puts "=> Rails #{Rails.version} application starting in #{Rails.env} #{url}" puts "=> Run `rails server -h` for more startup options" end @@ -91,6 +91,10 @@ module Rails def restart_command "bin/rails server #{ARGV.join(' ')}" end + + def use_puma? + server.to_s == "Rack::Handler::Puma" + end end module Command diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb index e7f51dba99..9705af8b40 100644 --- a/railties/lib/rails/generators/base.rb +++ b/railties/lib/rails/generators/base.rb @@ -16,6 +16,9 @@ module Rails include Thor::Actions include Rails::Generators::Actions + class_option :skip_namespace, type: :boolean, default: false, + desc: "Skip namespace (affects only isolated applications)" + add_runtime_options! strict_args_position! @@ -271,6 +274,40 @@ module Rails end end + # Wrap block with namespace of current application + # if namespace exists and is not skipped + def module_namespacing(&block) # :doc: + content = capture(&block) + content = wrap_with_namespace(content) if namespaced? + concat(content) + end + + def indent(content, multiplier = 2) # :doc: + spaces = " " * multiplier + content.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join + end + + def wrap_with_namespace(content) # :doc: + content = indent(content).chomp + "module #{namespace.name}\n#{content}\nend\n" + end + + def namespace # :doc: + Rails::Generators.namespace + end + + def namespaced? # :doc: + !options[:skip_namespace] && namespace + end + + def namespace_dirs + @namespace_dirs ||= namespace.name.split("::").map(&:underscore) + end + + def namespaced_path # :doc: + @namespaced_path ||= namespace_dirs.join("/") + end + # Use Rails default banner. def self.banner # :doc: "rails generate #{namespace.sub(/^rails:/, '')} #{arguments.map(&:usage).join(' ')} [options]".gsub(/\s+/, " ") diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index d63a5b0c30..dc1d6cab9d 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -6,8 +6,6 @@ module Rails module Generators class NamedBase < Base argument :name, type: :string - class_option :skip_namespace, type: :boolean, default: false, - desc: "Skip namespace (affects only isolated applications)" def initialize(args, *options) #:nodoc: @inside_template = nil @@ -45,24 +43,6 @@ module Rails file_name end - # Wrap block with namespace of current application - # if namespace exists and is not skipped - def module_namespacing(&block) # :doc: - content = capture(&block) - content = wrap_with_namespace(content) if namespaced? - concat(content) - end - - def indent(content, multiplier = 2) # :doc: - spaces = " " * multiplier - content.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join - end - - def wrap_with_namespace(content) # :doc: - content = indent(content).chomp - "module #{namespace.name}\n#{content}\nend\n" - end - def inside_template # :doc: @inside_template = true yield @@ -74,18 +54,6 @@ module Rails @inside_template end - def namespace # :doc: - Rails::Generators.namespace - end - - def namespaced? # :doc: - !options[:skip_namespace] && namespace - end - - def namespace_dirs - @namespace_dirs ||= namespace.name.split("::").map(&:underscore) - end - def file_path # :doc: @file_path ||= (class_path + [file_name]).join("/") end @@ -102,10 +70,6 @@ module Rails @namespaced_class_path ||= namespace_dirs + @class_path end - def namespaced_path # :doc: - @namespaced_path ||= namespace_dirs.join("/") - end - def class_name # :doc: (class_path + [file_name]).map!(&:camelize).join("::") end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index c63f23fa0a..9e612f1526 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -259,6 +259,13 @@ module ApplicationTests end end + test "db:schema:load fails if schema.rb doesn't exist yet" do + Dir.chdir(app_path) do + stderr_output = capture(:stderr) { `bin/rails db:schema:load` } + assert_match /Run `rails db:migrate` to create it/, stderr_output + end + end + def db_test_load_structure Dir.chdir(app_path) do `bin/rails generate model book title:string; |