diff options
90 files changed, 452 insertions, 260 deletions
@@ -37,8 +37,12 @@ gem "json", ">= 2.0.0" gem "rubocop", ">= 0.47", require: false +# https://github.com/guard/rb-inotify/pull/79 gem "rb-inotify", github: "matthewd/rb-inotify", branch: "close-handling", require: false +# https://github.com/puma/puma/pull/1345 +gem "stopgap_13632", platforms: :mri if %w(2.2.7 2.3.4 2.4.1).include? RUBY_VERSION + group :doc do gem "sdoc", github: "robin850/sdoc", branch: "upgrade" gem "redcarpet", "~> 3.2.3", platforms: :ruby diff --git a/Gemfile.lock b/Gemfile.lock index b9003f9de5..9da1aef756 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -438,6 +438,7 @@ GEM sqlite3 (1.3.13-x64-mingw32) sqlite3 (1.3.13-x86-mingw32) stackprof (0.2.10) + stopgap_13632 (1.0.1) sucker_punch (2.0.2) concurrent-ruby (~> 1.0.0) thin (1.7.2) @@ -535,6 +536,7 @@ DEPENDENCIES sprockets-export sqlite3 (~> 1.3.6) stackprof + stopgap_13632 sucker_punch turbolinks (~> 5) tzinfo-data diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index a54eb52dcb..1c1e1a9a3b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -59,7 +59,7 @@ module ActionMailer # The hash passed to the mail method allows you to specify any header that a <tt>Mail::Message</tt> # will accept (any valid email header including optional fields). # - # The mail method, if not passed a block, will inspect your views and send all the views with + # The +mail+ method, if not passed a block, will inspect your views and send all the views with # the same name as the method, so the above action would send the +welcome.text.erb+ view # file as well as the +welcome.html.erb+ view file in a +multipart/alternative+ email. # @@ -138,7 +138,7 @@ module ActionMailer # You can also define a <tt>default_url_options</tt> method on individual mailers to override these # default settings per-mailer. # - # By default when <tt>config.force_ssl</tt> is true, URLs generated for hosts will use the HTTPS protocol. + # By default when <tt>config.force_ssl</tt> is +true+, URLs generated for hosts will use the HTTPS protocol. # # = Sending mail # @@ -316,7 +316,7 @@ module ActionMailer # # = Callbacks # - # You can specify callbacks using before_action and after_action for configuring your messages. + # You can specify callbacks using <tt>before_action</tt> and <tt>after_action</tt> for configuring your messages. # This may be useful, for example, when you want to add default inline attachments for all # messages sent out by a certain mailer class: # diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index fe7265834f..a2ea45dc7b 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -4,7 +4,7 @@ require "delegate" module ActionMailer # The <tt>ActionMailer::MessageDelivery</tt> class is used by - # <tt>ActionMailer::Base</tt> when creating a new mailer. + # ActionMailer::Base when creating a new mailer. # <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy # created <tt>Mail::Message</tt>. You can get direct access to the # <tt>Mail::Message</tt>, deliver the email or schedule the email to be sent diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index 4a8d3659ec..730ac89c94 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -17,7 +17,7 @@ module ActionMailer # # config.action_mailer.show_previews = true # - # Defaults to true for development environment + # Defaults to +true+ for development environment # mattr_accessor :show_previews, instance_writer: false @@ -33,7 +33,7 @@ module ActionMailer # Register an Interceptor which will be called before mail is previewed. # Either a class or a string can be passed in as the Interceptor. If a - # string is passed in it will be <tt>constantize</tt>d. + # string is passed in it will be constantized. def register_preview_interceptor(interceptor) preview_interceptor = \ case interceptor @@ -81,12 +81,12 @@ module ActionMailer public_instance_methods(false).map(&:to_s).sort end - # Returns true if the email exists. + # Returns +true+ if the email exists. def email_exists?(email) emails.include?(email) end - # Returns true if the preview exists. + # Returns +true+ if the preview exists. def exists?(preview) all.any? { |p| p.preview_name == preview } end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 938b24a6b9..b56a1c5d4b 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,9 @@ +* Make `take_failed_screenshot` work within engine. + + Fixes #30405. + + *Yuji Yaginuma* + * Deprecate `ActionDispatch::TestResponse` response aliases `#success?`, `#missing?` & `#error?` are not supported by the actual diff --git a/actionpack/lib/action_dispatch/middleware/debug_locks.rb b/actionpack/lib/action_dispatch/middleware/debug_locks.rb index c61a941010..03760438f7 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_locks.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_locks.rb @@ -43,7 +43,7 @@ module ActionDispatch private def render_details(req) - threads = ActiveSupport::Dependencies.interlock.raw_state do |threads| + threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads| # The Interlock itself comes to a complete halt as long as this block # is executing. That gives us a more consistent picture of everything, # but creates a pretty strong Observer Effect. @@ -53,29 +53,29 @@ module ActionDispatch # strictly diagnostic tool (to be used when something has gone wrong), # and not for any sort of general monitoring. - threads.each.with_index do |(thread, info), idx| + raw_threads.each.with_index do |(thread, info), idx| info[:index] = idx info[:backtrace] = thread.backtrace end - threads + raw_threads end str = threads.map do |thread, info| if info[:exclusive] - lock_state = "Exclusive" + lock_state = "Exclusive".dup elsif info[:sharing] > 0 - lock_state = "Sharing" + lock_state = "Sharing".dup lock_state << " x#{info[:sharing]}" if info[:sharing] > 1 else - lock_state = "No lock" + lock_state = "No lock".dup end if info[:waiting] lock_state << " (yielded share)" end - msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n" + msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n".dup if info[:sleeper] msg << " Waiting in #{info[:sleeper]}" diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 57a5bc681e..2b43ade081 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -473,7 +473,7 @@ module ActionDispatch # <tt>params[<:param>]</tt>. # In your router: # - # resources :user, param: :name + # resources :users, param: :name # # You can override <tt>ActiveRecord::Base#to_param</tt> of a related # model to construct a URL: diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb index 7efdeb4e7b..6c337cdc31 100644 --- a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb @@ -44,11 +44,15 @@ module ActionDispatch end def image_path - "tmp/screenshots/#{image_name}.png" + @image_path ||= absolute_image_path.relative_path_from(Pathname.pwd).to_s + end + + def absolute_image_path + Rails.root.join("tmp/screenshots/#{image_name}.png") end def save_image - page.save_screenshot(Rails.root.join(image_path)) + page.save_screenshot(absolute_image_path) end def output_type @@ -69,10 +73,10 @@ module ActionDispatch case output_type when "artifact" - message << "\e]1338;url=artifact://#{image_path}\a\n" + message << "\e]1338;url=artifact://#{absolute_image_path}\a\n" when "inline" - name = inline_base64(File.basename(image_path)) - image = inline_base64(File.read(image_path)) + name = inline_base64(File.basename(absolute_image_path)) + image = inline_base64(File.read(absolute_image_path)) message << "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a\n" end diff --git a/actionpack/test/dispatch/debug_locks_test.rb b/actionpack/test/dispatch/debug_locks_test.rb new file mode 100644 index 0000000000..d69614bd79 --- /dev/null +++ b/actionpack/test/dispatch/debug_locks_test.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class DebugLocksTest < ActionDispatch::IntegrationTest + setup do + build_app + end + + def test_render_threads_status + thread_ready = Concurrent::CountDownLatch.new + test_terminated = Concurrent::CountDownLatch.new + + thread = Thread.new do + ActiveSupport::Dependencies.interlock.running do + thread_ready.count_down + test_terminated.wait + end + end + + thread_ready.wait + + get "/rails/locks" + + test_terminated.count_down + + assert_match(/Thread.*?Sharing/, @response.body) + ensure + thread.join + end + + private + def build_app + @app = self.class.build_app do |middleware| + middleware.use ActionDispatch::DebugLocks + end + end +end diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb index 4b49a42317..2afda31cf5 100644 --- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb +++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb @@ -8,23 +8,29 @@ class ScreenshotHelperTest < ActiveSupport::TestCase test "image path is saved in tmp directory" do new_test = DrivenBySeleniumWithChrome.new("x") - assert_equal "tmp/screenshots/x.png", new_test.send(:image_path) + Rails.stub :root, Pathname.getwd do + assert_equal "tmp/screenshots/x.png", new_test.send(:image_path) + end end test "image path includes failures text if test did not pass" do new_test = DrivenBySeleniumWithChrome.new("x") - new_test.stub :passed?, false do - assert_equal "tmp/screenshots/failures_x.png", new_test.send(:image_path) + Rails.stub :root, Pathname.getwd do + new_test.stub :passed?, false do + assert_equal "tmp/screenshots/failures_x.png", new_test.send(:image_path) + end end end test "image path does not include failures text if test skipped" do new_test = DrivenBySeleniumWithChrome.new("x") - new_test.stub :passed?, false do - new_test.stub :skipped?, true do - assert_equal "tmp/screenshots/x.png", new_test.send(:image_path) + Rails.stub :root, Pathname.getwd do + new_test.stub :passed?, false do + new_test.stub :skipped?, true do + assert_equal "tmp/screenshots/x.png", new_test.send(:image_path) + end end end end @@ -36,13 +42,23 @@ class ScreenshotHelperTest < ActiveSupport::TestCase new_test = DrivenBySeleniumWithChrome.new("x") - new_test.stub :passed?, false do - assert_match "\e]1338;url=artifact://tmp/screenshots/failures_x.png\a", new_test.send(:display_image) + Rails.stub :root, Pathname.getwd do + new_test.stub :passed?, false do + assert_match %r|url=artifact://.+?tmp/screenshots/failures_x\.png|, new_test.send(:display_image) + end end ensure ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = original_output_type end end + + test "image path returns the relative path from current directory" do + new_test = DrivenBySeleniumWithChrome.new("x") + + Rails.stub :root, Pathname.getwd.join("..") do + assert_equal "../tmp/screenshots/x.png", new_test.send(:image_path) + end + end end class RackTestScreenshotsTest < DrivenByRackTest diff --git a/actionview/lib/action_view/helpers/active_model_helper.rb b/actionview/lib/action_view/helpers/active_model_helper.rb index 46b514012c..f1ef715710 100644 --- a/actionview/lib/action_view/helpers/active_model_helper.rb +++ b/actionview/lib/action_view/helpers/active_model_helper.rb @@ -5,7 +5,7 @@ require "active_support/core_ext/enumerable" module ActionView # = Active Model Helpers - module Helpers + module Helpers #:nodoc: module ActiveModelHelper end diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index 5ba2370efe..a4dcfc9a6c 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -4,7 +4,7 @@ require "zlib" module ActionView # = Action View Asset URL Helpers - module Helpers + module Helpers #:nodoc: # This module provides methods for generating asset paths and # urls. # diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb index 34245b5df5..e6b9878271 100644 --- a/actionview/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb @@ -4,7 +4,7 @@ require "set" module ActionView # = Action View Atom Feed Helpers - module Helpers + module Helpers #:nodoc: module AtomFeedHelper # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other # template languages). diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb index cd213ad6a2..3cbb1ed1a7 100644 --- a/actionview/lib/action_view/helpers/cache_helper.rb +++ b/actionview/lib/action_view/helpers/cache_helper.rb @@ -2,7 +2,7 @@ module ActionView # = Action View Cache Helper - module Helpers + module Helpers #:nodoc: module CacheHelper # This helper exposes a method for caching fragments of a view # rather than an entire action or page. This technique is useful @@ -111,9 +111,9 @@ module ActionView # <%= render_categorizable_events @person.events %> # # This marks every template in the directory as a dependency. To find those - # templates, the wildcard path must be absolutely defined from app/views or paths + # templates, the wildcard path must be absolutely defined from <tt>app/views</tt> or paths # otherwise added with +prepend_view_path+ or +append_view_path+. - # This way the wildcard for `app/views/recordings/events` would be `recordings/events/*` etc. + # This way the wildcard for <tt>app/views/recordings/events</tt> would be <tt>recordings/events/*</tt> etc. # # The pattern used to match explicit dependencies is <tt>/# Template Dependency: (\S+)/</tt>, # so it's important that you type it out just so. @@ -133,14 +133,14 @@ module ActionView # # === Collection Caching # - # When rendering a collection of objects that each use the same partial, a `cached` + # When rendering a collection of objects that each use the same partial, a <tt>:cached</tt> # option can be passed. # # For collections rendered such: # # <%= render partial: 'projects/project', collection: @projects, cached: true %> # - # The `cached: true` will make Action View's rendering read several templates + # The <tt>cached: true</tt> will make Action View's rendering read several templates # from cache at once instead of one call per template. # # Templates in the collection not already cached are written to cache. diff --git a/actionview/lib/action_view/helpers/capture_helper.rb b/actionview/lib/action_view/helpers/capture_helper.rb index 690ce6a0ee..92f7ddb70d 100644 --- a/actionview/lib/action_view/helpers/capture_helper.rb +++ b/actionview/lib/action_view/helpers/capture_helper.rb @@ -4,12 +4,12 @@ require "active_support/core_ext/string/output_safety" module ActionView # = Action View Capture Helper - module Helpers + module Helpers #:nodoc: # CaptureHelper exposes methods to let you extract generated markup which # can be used in other parts of a template or layout file. # # It provides a method to capture blocks into variables through capture and - # a way to capture a block of markup for use in a layout through content_for. + # a way to capture a block of markup for use in a layout through {content_for}[rdoc-ref:ActionView::Helpers::CaptureHelper#content_for]. module CaptureHelper # The capture method extracts part of a template as a String object. # You can then use this object anywhere in your templates, layout, or helpers. @@ -44,7 +44,7 @@ module ActionView end end - # Calling content_for stores a block of markup in an identifier for later use. + # Calling <tt>content_for</tt> stores a block of markup in an identifier for later use. # In order to access this stored content in other templates, helper modules # or the layout, you would pass the identifier as an argument to <tt>content_for</tt>. # @@ -110,7 +110,7 @@ module ActionView # That will place +script+ tags for your default set of JavaScript files on the page; # this technique is useful if you'll only be using these scripts in a few views. # - # Note that content_for concatenates (default) the blocks it is given for a particular + # Note that <tt>content_for</tt> concatenates (default) the blocks it is given for a particular # identifier in order. For example: # # <% content_for :navigation do %> @@ -127,7 +127,7 @@ module ActionView # # <ul><%= content_for :navigation %></ul> # - # If the flush parameter is true content_for replaces the blocks it is given. For example: + # If the flush parameter is +true+ <tt>content_for</tt> replaces the blocks it is given. For example: # # <% content_for :navigation do %> # <li><%= link_to 'Home', action: 'index' %></li> @@ -147,7 +147,7 @@ module ActionView # # <% content_for :script, javascript_include_tag(:defaults) %> # - # WARNING: content_for is ignored in caches. So you shouldn't use it for elements that will be fragment cached. + # WARNING: <tt>content_for</tt> is ignored in caches. So you shouldn't use it for elements that will be fragment cached. def content_for(name, content = nil, options = {}, &block) if content || block_given? if block_given? @@ -174,7 +174,7 @@ module ActionView result unless content end - # content_for? checks whether any content has been captured yet using `content_for`. + # <tt>content_for?</tt> checks whether any content has been captured yet using <tt>content_for</tt>. # Useful to render parts of your layout differently based on what is in your views. # # <%# This is the layout %> diff --git a/actionview/lib/action_view/helpers/controller_helper.rb b/actionview/lib/action_view/helpers/controller_helper.rb index 00d8b9665d..79cf86c7d1 100644 --- a/actionview/lib/action_view/helpers/controller_helper.rb +++ b/actionview/lib/action_view/helpers/controller_helper.rb @@ -3,7 +3,7 @@ require "active_support/core_ext/module/attr_internal" module ActionView - module Helpers + module Helpers #:nodoc: # This module keeps all methods and behavior in ActionView # that simply delegates to the controller. module ControllerHelper #:nodoc: diff --git a/actionview/lib/action_view/helpers/csrf_helper.rb b/actionview/lib/action_view/helpers/csrf_helper.rb index d1b83f87d3..69c59844a6 100644 --- a/actionview/lib/action_view/helpers/csrf_helper.rb +++ b/actionview/lib/action_view/helpers/csrf_helper.rb @@ -2,7 +2,7 @@ module ActionView # = Action View CSRF Helper - module Helpers + module Helpers #:nodoc: module CsrfHelper # Returns meta tags "csrf-param" and "csrf-token" with the name of the cross-site # request forgery protection parameter and token, respectively. diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 339d582f76..642bd0fec6 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -9,7 +9,7 @@ require "active_support/core_ext/object/acts_like" require "active_support/core_ext/object/with_options" module ActionView - module Helpers + module Helpers #:nodoc: # = Action View Date Helpers # # The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time diff --git a/actionview/lib/action_view/helpers/debug_helper.rb b/actionview/lib/action_view/helpers/debug_helper.rb index 232d688077..52dff1f750 100644 --- a/actionview/lib/action_view/helpers/debug_helper.rb +++ b/actionview/lib/action_view/helpers/debug_helper.rb @@ -4,7 +4,7 @@ module ActionView # = Action View Debug Helper # # Provides a set of methods for making it easier to debug Rails objects. - module Helpers + module Helpers #:nodoc: module DebugHelper include TagHelper diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index bcda066837..6702c65ccb 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -14,7 +14,7 @@ require "active_support/core_ext/string/inflections" module ActionView # = Action View Form Helpers - module Helpers + module Helpers #:nodoc: # Form helpers are designed to make working with resources much easier # compared to using vanilla HTML. # diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb index 0f1a452652..1517abfad0 100644 --- a/actionview/lib/action_view/helpers/form_options_helper.rb +++ b/actionview/lib/action_view/helpers/form_options_helper.rb @@ -9,7 +9,7 @@ require "active_support/core_ext/array/wrap" module ActionView # = Action View Form Option Helpers - module Helpers + module Helpers #:nodoc: # Provides a number of methods for turning different kinds of containers into a set of option tags. # # The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash: diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 14cbed24b0..31a1f8be8c 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -7,7 +7,7 @@ require "active_support/core_ext/module/attribute_accessors" module ActionView # = Action View Form Tag Helpers - module Helpers + module Helpers #:nodoc: # Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like # FormHelper does. Instead, you provide the names and values manually. # @@ -456,7 +456,7 @@ module ActionView # submit tag but it isn't supported in legacy browsers. However, # the button tag does allow for richer labels such as images and emphasis, # so this helper will also accept a block. By default, it will create - # a button tag with type `submit`, if type is not given. + # a button tag with type <tt>submit</tt>, if type is not given. # # ==== Options # * <tt>:data</tt> - This option can be used to add custom data attributes. diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb index cce7c1dcfd..11eefe0ee0 100644 --- a/actionview/lib/action_view/helpers/javascript_helper.rb +++ b/actionview/lib/action_view/helpers/javascript_helper.rb @@ -3,7 +3,7 @@ require_relative "tag_helper" module ActionView - module Helpers + module Helpers #:nodoc: module JavaScriptHelper JS_ESCAPE_MAP = { '\\' => '\\\\', diff --git a/actionview/lib/action_view/helpers/record_tag_helper.rb b/actionview/lib/action_view/helpers/record_tag_helper.rb index 4303de4209..a6953ee905 100644 --- a/actionview/lib/action_view/helpers/record_tag_helper.rb +++ b/actionview/lib/action_view/helpers/record_tag_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ActionView - module Helpers + module Helpers #:nodoc: module RecordTagHelper def div_for(*) # :nodoc: raise NoMethodError, "The `div_for` method has been removed from " \ diff --git a/actionview/lib/action_view/helpers/rendering_helper.rb b/actionview/lib/action_view/helpers/rendering_helper.rb index 2702fa28b2..8e505ab054 100644 --- a/actionview/lib/action_view/helpers/rendering_helper.rb +++ b/actionview/lib/action_view/helpers/rendering_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ActionView - module Helpers + module Helpers #:nodoc: # = Action View Rendering # # Implements methods that allow rendering from a view context. diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb index 713b2fbb45..275a2dffb4 100644 --- a/actionview/lib/action_view/helpers/sanitize_helper.rb +++ b/actionview/lib/action_view/helpers/sanitize_helper.rb @@ -5,7 +5,7 @@ require "rails-html-sanitizer" module ActionView # = Action View Sanitize Helpers - module Helpers + module Helpers #:nodoc: # The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements. # These helper methods extend Action View making them callable within your template files. module SanitizeHelper diff --git a/actionview/lib/action_view/helpers/tags.rb b/actionview/lib/action_view/helpers/tags.rb index 2552ebba4e..566668b958 100644 --- a/actionview/lib/action_view/helpers/tags.rb +++ b/actionview/lib/action_view/helpers/tags.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ActionView - module Helpers + module Helpers #:nodoc: module Tags #:nodoc: extend ActiveSupport::Autoload diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index 75c1161de1..e663892592 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -6,7 +6,7 @@ require "i18n/exceptions" module ActionView # = Action View Translation Helpers - module Helpers + module Helpers #:nodoc: module TranslationHelper extend ActiveSupport::Concern diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index f2edcb750c..beb0a18b65 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -52,12 +52,12 @@ module ActionView # <%= render partial: "ad", locals: { ad: ad } %> # <% end %> # - # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then - # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. + # This would first render <tt>advertiser/_account.html.erb</tt> with <tt>@buyer</tt> passed in as the local variable +account+, then + # render <tt>advertiser/_ad.html.erb</tt> and pass the local variable +ad+ to the template for display. # # == The :as and :object options # - # By default <tt>ActionView::PartialRenderer</tt> doesn't have any local variables. + # By default ActionView::PartialRenderer doesn't have any local variables. # The <tt>:object</tt> option can be used to pass an object to the partial. For instance: # # <%= render partial: "account", object: @buyer %> @@ -85,7 +85,7 @@ module ActionView # # <%= render partial: "ad", collection: @advertisements %> # - # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An + # This will render <tt>advertiser/_ad.html.erb</tt> and pass the local variable +ad+ to the template for display. An # iteration object will automatically be made available to the template with a name of the form # +partial_name_iteration+. The iteration object has knowledge about which index the current object has in # the collection and the total size of the collection. The iteration object also has two convenience methods, @@ -100,7 +100,7 @@ module ActionView # # <%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %> # - # If the given <tt>:collection</tt> is +nil+ or empty, <tt>render</tt> will return nil. This will allow you + # If the given <tt>:collection</tt> is +nil+ or empty, <tt>render</tt> will return +nil+. This will allow you # to specify a text which will be displayed instead by using this form: # # <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %> @@ -114,18 +114,18 @@ module ActionView # # <%= render partial: "advertisement/ad", locals: { ad: @advertisement } %> # - # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from. + # This will render the partial <tt>advertisement/_ad.html.erb</tt> regardless of which controller this is being called from. # - # == \Rendering objects that respond to `to_partial_path` + # == \Rendering objects that respond to +to_partial_path+ # # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work - # and pick the proper path by checking `to_partial_path` method. + # and pick the proper path by checking +to_partial_path+ method. # # # @account.to_partial_path returns 'accounts/account', so it can be used to replace: # # <%= render partial: "accounts/account", locals: { account: @account} %> # <%= render partial: @account %> # - # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`, + # # @posts is an array of Post instances, so every post record returns 'posts/post' on +to_partial_path+, # # that's why we can replace: # # <%= render partial: "posts/post", collection: @posts %> # <%= render partial: @posts %> @@ -145,7 +145,7 @@ module ActionView # # <%= render partial: "accounts/account", locals: { account: @account} %> # <%= render @account %> # - # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`, + # # @posts is an array of Post instances, so every post record returns 'posts/post' on +to_partial_path+, # # that's why we can replace: # # <%= render partial: "posts/post", collection: @posts %> # <%= render @posts %> diff --git a/actionview/lib/action_view/template/handlers.rb b/actionview/lib/action_view/template/handlers.rb index b3df0aa606..7ec76dcc3f 100644 --- a/actionview/lib/action_view/template/handlers.rb +++ b/actionview/lib/action_view/template/handlers.rb @@ -2,7 +2,7 @@ module ActionView #:nodoc: # = Action View Template Handlers - class Template + class Template #:nodoc: module Handlers #:nodoc: autoload :Raw, "action_view/template/handlers/raw" autoload :ERB, "action_view/template/handlers/erb" diff --git a/actionview/lib/action_view/template/html.rb b/actionview/lib/action_view/template/html.rb index 540597efa1..a262c6d9ad 100644 --- a/actionview/lib/action_view/template/html.rb +++ b/actionview/lib/action_view/template/html.rb @@ -2,7 +2,7 @@ module ActionView #:nodoc: # = Action View HTML Template - class Template + class Template #:nodoc: class HTML #:nodoc: attr_accessor :type diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 708dee3164..a58d375293 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -310,13 +310,13 @@ module ActionView # ==== Examples # # Default pattern, loads views the same way as previous versions of rails, eg. when you're - # looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}` + # looking for <tt>users/new</tt> it will produce query glob: <tt>users/new{.{en},}{.{html,js},}{.{erb,haml},}</tt> # # FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}") # # This one allows you to keep files with different formats in separate subdirectories, - # eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`, - # `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc. + # eg. <tt>users/new.html</tt> will be loaded from <tt>users/html/new.erb</tt> or <tt>users/new.html.erb</tt>, + # <tt>users/new.js</tt> from <tt>users/js/new.erb</tt> or <tt>users/new.js.erb</tt>, etc. # # FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}") # diff --git a/actionview/lib/action_view/template/text.rb b/actionview/lib/action_view/template/text.rb index f070005881..f8d6c2811f 100644 --- a/actionview/lib/action_view/template/text.rb +++ b/actionview/lib/action_view/template/text.rb @@ -2,7 +2,7 @@ module ActionView #:nodoc: # = Action View Text Template - class Template + class Template #:nodoc: class Text #:nodoc: attr_accessor :type diff --git a/actionview/lib/action_view/template/types.rb b/actionview/lib/action_view/template/types.rb index f0f37c9722..67b7a62de6 100644 --- a/actionview/lib/action_view/template/types.rb +++ b/actionview/lib/action_view/template/types.rb @@ -3,7 +3,7 @@ require "active_support/core_ext/module/attribute_accessors" module ActionView - class Template + class Template #:nodoc: class Types class Type SET = Struct.new(:symbols).new([ :html, :text, :js, :css, :xml, :json ]) diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb index ad32d3065b..c0d016853c 100644 --- a/activejob/lib/active_job/enqueuing.rb +++ b/activejob/lib/active_job/enqueuing.rb @@ -10,7 +10,7 @@ module ActiveJob # Includes the +perform_later+ method for job initialization. module ClassMethods # Push a job onto the queue. The arguments must be legal JSON types - # (string, int, float, nil, true, false, hash or array) or + # (+string+, +int+, +float+, +nil+, +true+, +false+, +hash+ or +array+) or # GlobalID::Identification instances. Arbitrary Ruby objects # are not supported. # diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb index 5768eec7f5..8fa9680cb1 100644 --- a/activemodel/lib/active_model/callbacks.rb +++ b/activemodel/lib/active_model/callbacks.rb @@ -103,8 +103,8 @@ module ActiveModel # end # end # - # NOTE: +method_name+ passed to `define_model_callbacks` must not end with - # `!`, `?` or `=`. + # NOTE: +method_name+ passed to define_model_callbacks must not end with + # <tt>!</tt>, <tt>?</tt> or <tt>=</tt>. def define_model_callbacks(*callbacks) options = callbacks.extract_options! options = { diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 1a1a3a8092..b421fedc96 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Add new error class `TransactionTimeout` for MySQL adapter which will be raised + when lock wait time expires. + + *Gabriel Courtemanche* + * Remove deprecated `#migration_keys`. *Ryuta Kamizono* diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index c622df132e..4f957ac3ca 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -94,7 +94,7 @@ module ActiveRecord end # Did this attribute change when we last saved? This method can be invoked - # as `saved_change_to_name?` instead of `saved_change_to_attribute?("name")`. + # as +saved_change_to_name?+ instead of <tt>saved_change_to_attribute?("name")</tt>. # Behaves similarly to +attribute_changed?+. This method is useful in # after callbacks to determine if the call to save changed a certain # attribute. @@ -117,8 +117,8 @@ module ActiveRecord # Behaves similarly to +attribute_change+. This method is useful in after # callbacks, to see the change in an attribute that just occurred # - # This method can be invoked as `saved_change_to_name` in instead of - # `saved_change_to_attribute("name")` + # This method can be invoked as +saved_change_to_name+ in instead of + # <tt>saved_change_to_attribute("name")</tt> def saved_change_to_attribute(attr_name) mutations_before_last_save.change_to_attribute(attr_name) end @@ -131,7 +131,7 @@ module ActiveRecord mutations_before_last_save.original_value(attr_name) end - # Did the last call to `save` have any changes to change? + # Did the last call to +save+ have any changes to change? def saved_changes? mutations_before_last_save.any_changes? end @@ -141,37 +141,37 @@ module ActiveRecord mutations_before_last_save.changes end - # Alias for `attribute_changed?` + # Alias for +attribute_changed?+ def will_save_change_to_attribute?(attr_name, **options) mutations_from_database.changed?(attr_name, **options) end - # Alias for `attribute_change` + # Alias for +attribute_change+ def attribute_change_to_be_saved(attr_name) mutations_from_database.change_to_attribute(attr_name) end - # Alias for `attribute_was` + # Alias for +attribute_was+ def attribute_in_database(attr_name) mutations_from_database.original_value(attr_name) end - # Alias for `changed?` + # Alias for +changed?+ def has_changes_to_save? mutations_from_database.any_changes? end - # Alias for `changes` + # Alias for +changes+ def changes_to_save mutations_from_database.changes end - # Alias for `changed` + # Alias for +changed+ def changed_attribute_names_to_save changes_to_save.keys end - # Alias for `changed_attributes` + # Alias for +changed_attributes+ def attributes_in_database changes_to_save.transform_values(&:first) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index f36c1f111a..f57c7a5d4d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -406,6 +406,8 @@ module ActiveRecord # # Defaults to false. # + # Only supported on the MySQL adapter, ignored elsewhere. + # # ====== Add a column # # change_table(:suppliers) do |t| 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 3683522a0c..7cd086084a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -33,7 +33,7 @@ module ActiveRecord string: { name: "varchar", limit: 255 }, text: { name: "text", limit: 65535 }, integer: { name: "int", limit: 4 }, - float: { name: "float" }, + float: { name: "float", limit: 24 }, decimal: { name: "decimal" }, datetime: { name: "datetime" }, timestamp: { name: "timestamp" }, diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index d6f357bcc3..6fdd666486 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -76,7 +76,7 @@ module ActiveRecord ## # :singleton-method: # Indicates whether boolean values are stored in sqlite3 databases as 1 - # and 0 or 't' and 'f'. Leaving `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer` + # and 0 or 't' and 'f'. Leaving <tt>ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer</tt> # set to false is deprecated. SQLite databases have used 't' and 'f' to # serialize boolean values and must have old data converted to 1 and 0 # (its native boolean serialization) before setting this flag to true. @@ -85,7 +85,7 @@ module ActiveRecord # ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1) # ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0) # for all models and all boolean columns, after which the flag must be set - # to true by adding the following to your application.rb file: + # to true by adding the following to your <tt>application.rb</tt> file: # # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true class_attribute :represent_boolean_as_integer, default: false diff --git a/activerecord/lib/active_record/define_callbacks.rb b/activerecord/lib/active_record/define_callbacks.rb index 2c8783dcc9..87ecd7cec5 100644 --- a/activerecord/lib/active_record/define_callbacks.rb +++ b/activerecord/lib/active_record/define_callbacks.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true module ActiveRecord - # This module exists because `ActiveRecord::AttributeMethods::Dirty` needs to - # define callbacks, but continue to have its version of `save` be the super - # method of `ActiveRecord::Callbacks`. This will be removed when the removal + # This module exists because ActiveRecord::AttributeMethods::Dirty needs to + # define callbacks, but continue to have its version of +save+ be the super + # method of ActiveRecord::Callbacks. This will be removed when the removal # of deprecated code removes this need. module DefineCallbacks extend ActiveSupport::Concern diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 4940e122f4..12169fffa9 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -147,7 +147,7 @@ module ActiveRecord # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module # that is included in ActiveRecord::FixtureSet.context_class. # - # - define a helper method in `test_helper.rb` + # - define a helper method in <tt>test_helper.rb</tt> # module FixtureFileHelpers # def file_sha(path) # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path))) diff --git a/activerecord/lib/active_record/no_touching.rb b/activerecord/lib/active_record/no_touching.rb index c573deb63a..754c891884 100644 --- a/activerecord/lib/active_record/no_touching.rb +++ b/activerecord/lib/active_record/no_touching.rb @@ -6,7 +6,7 @@ module ActiveRecord extend ActiveSupport::Concern module ClassMethods - # Lets you selectively disable calls to `touch` for the + # Lets you selectively disable calls to +touch+ for the # duration of a block. # # ==== Examples diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 1297e0cde7..fbbf9082cc 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -353,7 +353,7 @@ module ActiveRecord # Wrapper around #increment that writes the update to the database. # Only +attribute+ is updated; the record itself is not saved. # This means that any other modified attributes will still be dirty. - # Validations and callbacks are skipped. Supports the `touch` option from + # Validations and callbacks are skipped. Supports the +touch+ option from # +update_counters+, see that for more. # Returns +self+. def increment!(attribute, by = 1, touch: nil) @@ -374,7 +374,7 @@ module ActiveRecord # Wrapper around #decrement that writes the update to the database. # Only +attribute+ is updated; the record itself is not saved. # This means that any other modified attributes will still be dirty. - # Validations and callbacks are skipped. Supports the `touch` option from + # Validations and callbacks are skipped. Supports the +touch+ option from # +update_counters+, see that for more. # Returns +self+. def decrement!(attribute, by = 1, touch: nil) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index e35049bb41..00ff20f4d7 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1036,20 +1036,12 @@ module ActiveRecord def scopes scopes = @previous_reflection.scopes - if @previous_reflection.options[:source_type] - scopes + [@previous_reflection.source_type_scope] - else - scopes - end + scopes << @previous_reflection.source_type_scope end def join_scopes(table, predicate_builder) # :nodoc: scopes = @previous_reflection.join_scopes(table, predicate_builder) + super - if @previous_reflection.options[:source_type] - scopes + [@previous_reflection.source_type_scope] - else - scopes - end + scopes << @previous_reflection.source_type_scope end def klass @@ -1100,10 +1092,6 @@ module ActiveRecord @reflection.constraints end - def alias_candidate(name) - "#{plural_name}_#{name}_join" - end - def alias_name Arel::Table.new(table_name, type_caster: klass.type_caster) end diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 4793f2a49b..1aa85993ca 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -75,13 +75,6 @@ module ActiveRecord end end end - - def delegate(method, opts = {}) - @delegation_mutex.synchronize do - return if method_defined?(method) - super - end - end end private @@ -93,7 +86,6 @@ module ActiveRecord elsif arel.respond_to?(method) ActiveSupport::Deprecation.warn \ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0." - self.class.delegate method, to: :arel arel.public_send(method, *args, &block) else super diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index d2e1892ae7..8d0311fabd 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -132,14 +132,13 @@ HEADER else tbl.print ", id: false" end - tbl.print ", force: :cascade" table_options = @connection.table_options(table) if table_options.present? tbl.print ", #{format_options(table_options)}" end - tbl.puts " do |t|" + tbl.puts ", force: :cascade do |t|" # then dump all non-primary key columns columns.each do |column| diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb index 5681ccdd23..647e066137 100644 --- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb @@ -117,7 +117,7 @@ module ActiveRecord end def run_cmd_error(cmd, args, action) - msg = "failed to execute:\n" + msg = "failed to execute:\n".dup msg << "#{cmd} #{args.join(' ')}\n\n" msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n" msg diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb index abdd6db64a..dfe599c4dd 100644 --- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb @@ -73,7 +73,7 @@ module ActiveRecord end def run_cmd_error(cmd, args) - msg = "failed to execute:\n" + msg = "failed to execute:\n".dup msg << "#{cmd} #{args.join(' ')}\n\n" msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n" msg diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb index 6183d66b63..9f6bd38a8c 100644 --- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb +++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb @@ -17,28 +17,28 @@ class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase test "table options with ENGINE" do @connection.create_table "mysql_table_options", force: true, options: "ENGINE=MyISAM" output = dump_table_schema("mysql_table_options") - options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options] assert_match %r{ENGINE=MyISAM}, options end test "table options with ROW_FORMAT" do @connection.create_table "mysql_table_options", force: true, options: "ROW_FORMAT=REDUNDANT" output = dump_table_schema("mysql_table_options") - options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options] assert_match %r{ROW_FORMAT=REDUNDANT}, options end test "table options with CHARSET" do @connection.create_table "mysql_table_options", force: true, options: "CHARSET=utf8mb4" output = dump_table_schema("mysql_table_options") - options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options] assert_match %r{CHARSET=utf8mb4}, options end test "table options with COLLATE" do @connection.create_table "mysql_table_options", force: true, options: "COLLATE=utf8mb4_bin" output = dump_table_schema("mysql_table_options") - options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options] assert_match %r{COLLATE=utf8mb4_bin}, options end end diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb index d0a09f6481..b01f5d7f5a 100644 --- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb +++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb @@ -62,7 +62,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase schema = dump_table_schema "unsigned_types" assert_match %r{t\.integer\s+"unsigned_integer",\s+unsigned: true$}, schema assert_match %r{t\.bigint\s+"unsigned_bigint",\s+unsigned: true$}, schema - assert_match %r{t\.float\s+"unsigned_float",\s+limit: 24,\s+unsigned: true$}, schema + assert_match %r{t\.float\s+"unsigned_float",\s+unsigned: true$}, schema assert_match %r{t\.decimal\s+"unsigned_decimal",\s+precision: 10,\s+scale: 2,\s+unsigned: true$}, schema end end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index da5e830ebc..4d8368fd8a 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -5,6 +5,7 @@ require "models/bird" require "models/post" require "models/comment" require "models/company" +require "models/contract" require "models/customer" require "models/developer" require "models/computer" @@ -12,9 +13,7 @@ require "models/invoice" require "models/line_item" require "models/order" require "models/parrot" -require "models/person" require "models/pirate" -require "models/reader" require "models/ship" require "models/ship_part" require "models/tag" @@ -496,7 +495,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib end class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase - fixtures :companies, :people + fixtures :companies, :developers def test_invalid_adding firm = Firm.find(1) @@ -591,12 +590,12 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa end def test_assign_ids_for_through_a_belongs_to - post = Post.new(title: "Assigning IDs works!", body: "You heard it here first, folks!") - post.person_ids = [people(:david).id, people(:michael).id] - post.save - post.reload - assert_equal 2, post.people.length - assert_includes post.people, people(:david) + firm = Firm.new("name" => "Apple") + firm.developer_ids = [developers(:david).id, developers(:jamis).id] + firm.save + firm.reload + assert_equal 2, firm.developers.length + assert_includes firm.developers, developers(:david) end def test_build_before_save diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb index f2ec5d6518..1bcafd4b55 100644 --- a/activerecord/test/cases/comment_test.rb +++ b/activerecord/test/cases/comment_test.rb @@ -111,7 +111,7 @@ if ActiveRecord::Base.connection.supports_comments? # And check that these changes are reflected in dump output = dump_table_schema "commenteds" - assert_match %r[create_table "commenteds",.+\s+comment: "A table with comment"], output + assert_match %r[create_table "commenteds",.*\s+comment: "A table with comment"], output assert_match %r[t\.string\s+"name",\s+comment: "Comment should help clarify the column purpose"], output assert_match %r[t\.string\s+"obvious"\n], output assert_match %r[t\.string\s+"content",\s+comment: "Whoa, content describes itself!"], output diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index df83fe0ea1..e0888f5fe9 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -434,7 +434,7 @@ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter) test "schema dump primary key with serial/integer" do @connection.create_table(:widgets, id: @pk_type, force: true) schema = dump_table_schema "widgets" - assert_match %r{create_table "widgets", id: :#{@pk_type}, force: :cascade}, schema + assert_match %r{create_table "widgets", id: :#{@pk_type}, }, schema end if current_adapter?(:Mysql2Adapter) @@ -447,7 +447,7 @@ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter) assert column.unsigned? schema = dump_table_schema "widgets" - assert_match %r{create_table "widgets", id: :integer, unsigned: true, force: :cascade}, schema + assert_match %r{create_table "widgets", id: :integer, unsigned: true, }, schema end test "bigint primary key with unsigned" do @@ -459,7 +459,7 @@ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter) assert column.unsigned? schema = dump_table_schema "widgets" - assert_match %r{create_table "widgets", id: :bigint, unsigned: true, force: :cascade}, schema + assert_match %r{create_table "widgets", id: :bigint, unsigned: true, }, schema end end end diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb index 8ffb9f163b..04d688ac53 100644 --- a/activerecord/test/cases/relation/delegation_test.rb +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -32,6 +32,7 @@ module ActiveRecord def test_deprecate_arel_delegation AREL_METHODS.each do |method| assert_deprecated { target.public_send(method) } + assert_deprecated { target.public_send(method) } end end end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index eb9b257da9..799a65c61e 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -66,7 +66,7 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_schema_dump_uses_force_cascade_on_create_table output = dump_table_schema "authors" - assert_match %r{create_table "authors", force: :cascade}, output + assert_match %r{create_table "authors",.* force: :cascade}, output end def test_schema_dump_excludes_sqlite_sequence @@ -207,20 +207,25 @@ class SchemaDumperTest < ActiveRecord::TestCase end def test_schema_dump_should_use_false_as_default - output = standard_dump + output = dump_table_schema "booleans" assert_match %r{t\.boolean\s+"has_fun",.+default: false}, output end def test_schema_dump_does_not_include_limit_for_text_field - output = standard_dump + output = dump_table_schema "admin_users" assert_match %r{t\.text\s+"params"$}, output end def test_schema_dump_does_not_include_limit_for_binary_field - output = standard_dump + output = dump_table_schema "binaries" assert_match %r{t\.binary\s+"data"$}, output end + def test_schema_dump_does_not_include_limit_for_float_field + output = dump_table_schema "numeric_data" + assert_match %r{t\.float\s+"temperature"$}, output + end + if current_adapter?(:Mysql2Adapter) def test_schema_dump_includes_length_for_mysql_binary_fields output = standard_dump diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index 693503250b..6302e84884 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -295,6 +295,16 @@ if current_adapter?(:PostgreSQLAdapter) end end + def test_structure_dump_execution_fails + filename = "awesome-file.sql" + Kernel.expects(:system).with("pg_dump", "-s", "-x", "-O", "-f", filename, "my-app-db").returns(nil) + + e = assert_raise(RuntimeError) do + ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) + end + assert_match("failed to execute:", e.message) + end + private def with_dump_schemas(value, &block) old_dump_schemas = ActiveRecord::Base.dump_schemas diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb index 8ac4878c37..d7e3caa2ff 100644 --- a/activerecord/test/cases/tasks/sqlite_rake_test.rb +++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb @@ -215,6 +215,31 @@ if current_adapter?(:SQLite3Adapter) FileUtils.rm_f(filename) FileUtils.rm_f(dbfile) end + + def test_structure_dump_execution_fails + dbfile = @database + filename = "awesome-file.sql" + Kernel.expects(:system).with("sqlite3", "--noop", "db_create.sqlite3", ".schema", out: "awesome-file.sql").returns(nil) + + e = assert_raise(RuntimeError) do + with_structure_dump_flags(["--noop"]) do + quietly { ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename, "/rails/root") } + end + end + assert_match("failed to execute:", e.message) + ensure + FileUtils.rm_f(filename) + FileUtils.rm_f(dbfile) + end + + private + def with_structure_dump_flags(flags) + old = ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = flags + yield + ensure + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = old + end end class SqliteStructureLoadTest < ActiveRecord::TestCase diff --git a/activestorage/app/jobs/active_storage/purge_job.rb b/activestorage/app/jobs/active_storage/purge_job.rb index 369c07929d..990ab27c9f 100644 --- a/activestorage/app/jobs/active_storage/purge_job.rb +++ b/activestorage/app/jobs/active_storage/purge_job.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -# Provides delayed purging of attachments or blobs using their +purge_later+ method. +# Provides delayed purging of ActiveStorage::Blob records via ActiveStorage::Blob#purge_later. class ActiveStorage::PurgeJob < ActiveJob::Base # FIXME: Limit this to a custom ActiveStorage error retry_on StandardError - def perform(attachment_or_blob) - attachment_or_blob.purge + def perform(blob) + blob.purge end end diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb index ad43845e4e..2a1c20b7db 100644 --- a/activestorage/app/models/active_storage/attachment.rb +++ b/activestorage/app/models/active_storage/attachment.rb @@ -14,17 +14,15 @@ class ActiveStorage::Attachment < ActiveRecord::Base delegate_missing_to :blob - # Purging an attachment will purge the blob (delete the file on the service, then destroy the record) - # and then destroy the attachment itself. + # Synchronously purges the blob (deletes it from the configured service) and destroys the attachment. def purge blob.purge destroy end - # Purging an attachment means purging the blob, which means talking to the service, which means - # talking over the Internet. Whenever you're doing that, it's a good idea to put that work in a job, - # so it doesn't hold up other operations. That's what +purge_later+ provides. + # Destroys the attachment and asynchronously purges the blob (deletes it from the configured service). def purge_later - ActiveStorage::PurgeJob.perform_later(self) + blob.purge_later + destroy end end diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 664a53a778..e6cf08ce83 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -3,8 +3,8 @@ # A blob is a record that contains the metadata about a file and a key for where that file resides on the service. # Blobs can be created in two ways: # -# 1) Subsequent to the file being uploaded server-side to the service via <tt>create_after_upload!</tt>. -# 2) Ahead of the file being directly uploaded client-side to the service via <tt>create_before_direct_upload!</tt>. +# 1. Subsequent to the file being uploaded server-side to the service via <tt>create_after_upload!</tt>. +# 2. Ahead of the file being directly uploaded client-side to the service via <tt>create_before_direct_upload!</tt>. # # The first option doesn't require any client-side JavaScript integration, and can be used by any other back-end # service that deals with files. The second option is faster, since you're not using your own server as a staging diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb index 10b1116d52..dead6b6d33 100644 --- a/activestorage/app/models/active_storage/filename.rb +++ b/activestorage/app/models/active_storage/filename.rb @@ -9,25 +9,33 @@ class ActiveStorage::Filename @filename = filename end - # Filename.new("racecar.jpg").base # => "racecar" + # Returns the basename of the filename. + # + # ActiveStorage::Filename.new("racecar.jpg").base # => "racecar" def base File.basename @filename, extension_with_delimiter end - # Filename.new("racecar.jpg").extension_with_delimiter # => ".jpg" + # Returns the extension with delimiter of the filename. + # + # ActiveStorage::Filename.new("racecar.jpg").extension_with_delimiter # => ".jpg" def extension_with_delimiter File.extname @filename end - # Filename.new("racecar.jpg").extension_without_delimiter # => "jpg" + # Returns the extension without delimiter of the filename. + # + # ActiveStorage::Filename.new("racecar.jpg").extension_without_delimiter # => "jpg" def extension_without_delimiter extension_with_delimiter.from(1).to_s end alias_method :extension, :extension_without_delimiter - # Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg" - # Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg" + # Returns the sanitized filename. + # + # ActiveStorage::Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg" + # ActiveStorage::Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg" # # ...and any other character unsafe for URLs or storage is converted or stripped. def sanitized diff --git a/activestorage/lib/active_storage/attached.rb b/activestorage/lib/active_storage/attached.rb index 9c4b6d5d1d..c08fd56652 100644 --- a/activestorage/lib/active_storage/attached.rb +++ b/activestorage/lib/active_storage/attached.rb @@ -8,10 +8,10 @@ module ActiveStorage # Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many # classes that both provide proxy access to the blob association for a record. class Attached - attr_reader :name, :record + attr_reader :name, :record, :dependent - def initialize(name, record) - @name, @record = name, record + def initialize(name, record, dependent:) + @name, @record, @dependent = name, record, dependent end private diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb index f3879ee2e3..35a081adc4 100644 --- a/activestorage/lib/active_storage/attached/macros.rb +++ b/activestorage/lib/active_storage/attached/macros.rb @@ -26,7 +26,7 @@ module ActiveStorage def has_one_attached(name, dependent: :purge_later) class_eval <<-CODE, __FILE__, __LINE__ + 1 def #{name} - @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self) + @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self, dependent: #{dependent == :purge_later ? ":purge_later" : "false"}) end CODE @@ -65,7 +65,7 @@ module ActiveStorage def has_many_attached(name, dependent: :purge_later) class_eval <<-CODE, __FILE__, __LINE__ + 1 def #{name} - @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self) + @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self, dependent: #{dependent == :purge_later ? ":purge_later" : "false"}) end CODE diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb index 2e5831348e..ac90f32d95 100644 --- a/activestorage/lib/active_storage/attached/one.rb +++ b/activestorage/lib/active_storage/attached/one.rb @@ -21,11 +21,14 @@ module ActiveStorage # person.avatar.attach(io: File.open("~/face.jpg"), filename: "face.jpg", content_type: "image/jpg") # person.avatar.attach(avatar_blob) # ActiveStorage::Blob object def attach(attachable) - write_attachment \ - ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable)) + if attached? && dependent == :purge_later + replace attachable + else + write_attachment create_attachment_from(attachable) + end end - # Returns true if an attachment has been made. + # Returns +true+ if an attachment has been made. # # class User < ActiveRecord::Base # has_one_attached :avatar @@ -53,6 +56,19 @@ module ActiveStorage end private + def replace(attachable) + blob.tap do + transaction do + destroy + write_attachment create_attachment_from(attachable) + end + end.purge_later + end + + def create_attachment_from(attachable) + ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable)) + end + def write_attachment(attachment) record.public_send("#{name}_attachment=", attachment) end diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb index ce736b8728..b80fdea1ab 100644 --- a/activestorage/lib/active_storage/service.rb +++ b/activestorage/lib/active_storage/service.rb @@ -77,7 +77,7 @@ module ActiveStorage raise NotImplementedError end - # Return true if a file exists at the +key+. + # Return +true+ if a file exists at the +key+. def exist?(key) raise NotImplementedError end diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb index 5aa8d74a5a..f600753a08 100644 --- a/activestorage/lib/active_storage/service/disk_service.rb +++ b/activestorage/lib/active_storage/service/disk_service.rb @@ -66,7 +66,7 @@ module ActiveStorage verified_key_with_expiration, filename: filename, disposition: disposition, content_type: content_type else - "/rails/active_storage/disk/#{verified_key_with_expiration}/#{filename}?disposition=#{disposition}&content_type=#{content_type}" + "/rails/active_storage/disk/#{verified_key_with_expiration}/#{filename}?content_type=#{content_type}&disposition=#{disposition}" end payload[:url] = generated_url diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb index 7cfd8683db..ac346c0087 100644 --- a/activestorage/test/models/attachments_test.rb +++ b/activestorage/test/models/attachments_test.rb @@ -31,6 +31,31 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase assert_equal "racecar.jpg", @user.avatar.filename.to_s end + test "replace attached blob" do + @user.avatar.attach create_blob(filename: "funky.jpg") + + perform_enqueued_jobs do + assert_no_difference -> { ActiveStorage::Blob.count } do + @user.avatar.attach create_blob(filename: "town.jpg") + end + end + + assert_equal "town.jpg", @user.avatar.filename.to_s + end + + test "replace attached blob unsuccessfully" do + @user.avatar.attach create_blob(filename: "funky.jpg") + + perform_enqueued_jobs do + assert_raises do + @user.avatar.attach nil + end + end + + assert_equal "funky.jpg", @user.reload.avatar.filename.to_s + assert ActiveStorage::Blob.service.exist?(@user.avatar.key) + end + test "access underlying associations of new blob" do @user.avatar.attach create_blob(filename: "funky.jpg") assert_equal @user, @user.avatar_attachment.record diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb index f57e44536a..e07d1d88bc 100644 --- a/activestorage/test/service/disk_service_test.rb +++ b/activestorage/test/service/disk_service_test.rb @@ -8,7 +8,7 @@ class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase include ActiveStorage::Service::SharedServiceTests test "url generation" do - assert_match(/rails\/active_storage\/disk\/.*\/avatar\.png\?.+disposition=inline/, + assert_match(/rails\/active_storage\/disk\/.*\/avatar\.png\?content_type=image%2Fpng&disposition=inline/, @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: "inline; filename=\"avatar.png\"", filename: "avatar.png", content_type: "image/png")) end end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 3ffe21e559..ad2f21205f 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -85,7 +85,7 @@ module ActiveSupport #:nodoc: # handles the new constants. # # If child.rb is being autoloaded, its constants will be added to - # autoloaded_constants. If it was being `require`d, they will be discarded. + # autoloaded_constants. If it was being required, they will be discarded. # # This is handled by walking back up the watch stack and adding the constants # found by child.rb to the list of original constants in parent.rb. diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md index 6d53e1c2b4..3805fd2a63 100644 --- a/guides/source/5_0_release_notes.md +++ b/guides/source/5_0_release_notes.md @@ -55,7 +55,7 @@ information. ### API Applications Rails can now be used to create slimmed down API only applications. -This is useful for creating and serving APIs similar to [Twitter](https://dev.twitter.com) or [GitHub](https://developer.github.com) API, +This is useful for creating and serving APIs similar to [Twitter](https://dev.twitter.com) or [GitHub](https://developer.github.com) API, that can be used to serve public facing, as well as, for custom applications. You can generate a new api Rails app using: @@ -77,7 +77,7 @@ This will do three main things: you generate a new resource. The application provides a base for APIs, -that can then be [configured to pull in functionality](api_app.html) as suitable for the application's needs. +that can then be [configured to pull in functionality](api_app.html) as suitable for the application's needs. See the [Using Rails for API-only Applications](api_app.html) guide for more information. diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index 038148f75b..96ef9c4450 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -413,7 +413,7 @@ inside of Action Controller, so you can use all the same options, such as #### Caching mailer view -You can perform fragment caching in mailer views like in application views using the `cache` method. +You can perform fragment caching in mailer views like in application views using the `cache` method. ``` <% cache do %> diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 53cacfa6ea..678b80516f 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -1393,7 +1393,7 @@ end ``` NOTE: The `default_scope` is also applied while creating/building a record -when the scope arguments are given as a `Hash`. It is not applied while +when the scope arguments are given as a `Hash`. It is not applied while updating a record. E.g.: ```ruby diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 20d09e65b3..9f89e666dc 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -135,9 +135,9 @@ NOTE: Defined in `active_support/core_ext/object/blank.rb`. ### `duplicable?` -In Ruby 2.4 most objects can be duplicated via `dup` or `clone` except +In Ruby 2.4 most objects can be duplicated via `dup` or `clone` except methods and certain numbers. Though Ruby 2.2 and 2.3 can't duplicate `nil`, -`false`, `true`, and symbols as well as instances `Float`, `Fixnum`, +`false`, `true`, and symbols as well as instances `Float`, `Fixnum`, and `Bignum` instances. ```ruby diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 6cdce5c2f4..910a531068 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -175,10 +175,28 @@ class Game < ApplicationRecord end ``` -With `touch` set to true, any action which changes `updated_at` for a game +With `touch` set to `true`, any action which changes `updated_at` for a game record will also change it for the associated product, thereby expiring the cache. +### Shared Partial Caching + +It is possible to share partials and associated caching between files with different mime types. For example shared partial caching allows template writers to share a partial between HTML and Javascript files. When templates are collected in the template resolver file paths they only include the template language extension and not the mime type. Because of this templates can be used for multiple mime types. Both HTML and JavaScript requests will respond to the following code: + +```ruby +render(partial: 'hotels/hotel', collection: @hotels, cached: true) +``` + +Will load a file named `hotels/hotel.erb`. + +Another option is to include the full filename of the partial to render. + +```ruby +render(partial: 'hotels/hotel.html.erb', collection: @hotels, cached: true) +``` + +Will load a file named `hotels/hotel.html.erb` in any file mime type, for example you could include this partial in a Javascript file. + ### Managing dependencies In order to correctly invalidate the cache, you need to properly define the @@ -272,7 +290,7 @@ Sometimes you need to cache a particular value or query result instead of cachin The most efficient way to implement low-level caching is using the `Rails.cache.fetch` method. This method does both reading and writing to the cache. When passed only a single argument, the key is fetched and value from the cache is returned. If a block is passed, that block will be executed in the event of a cache miss. The return value of the block will be written to the cache under the given cache key, and that return value will be returned. In case of cache hit, the cached value will be returned without executing the block. -Consider the following example. An application has a `Product` model with an instance method that looks up the product’s price on a competing website. The data returned by this method would be perfect for low-level caching: +Consider the following example. An application has a `Product` model with an instance method that looks up the product's price on a competing website. The data returned by this method would be perfect for low-level caching: ```ruby class Product < ApplicationRecord @@ -284,7 +302,7 @@ class Product < ApplicationRecord end ``` -NOTE: Notice that in this example we used the `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key. +NOTE: Notice that in this example we used the `cache_key` method, so the resulting cache key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model's `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key. ### SQL Caching @@ -387,9 +405,9 @@ store is not appropriate for large application deployments. However, it can work well for small, low traffic sites with only a couple of server processes, as well as development and test environments. -New Rails projects are configured to use this implementation in development environment by default. +New Rails projects are configured to use this implementation in development environment by default. -NOTE: Since processes will not share cache data when using `:memory_store`, +NOTE: Since processes will not share cache data when using `:memory_store`, it will not be possible to manually read, write or expire the cache via the Rails console. ### ActiveSupport::Cache::FileStore @@ -580,7 +598,7 @@ Caching in Development ---------------------- It's common to want to test the caching strategy of your application -in development mode. Rails provides the rake task `dev:cache` to +in development mode. Rails provides the rake task `dev:cache` to easily toggle caching on/off. ```bash diff --git a/guides/source/i18n.md b/guides/source/i18n.md index dda16f755e..cb24822f86 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -105,7 +105,7 @@ This means, that in the `:en` locale, the key _hello_ will map to the _Hello wor The I18n library will use **English** as a **default locale**, i.e. if a different locale is not set, `:en` will be used for looking up translations. -NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en)), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Few gems such as [Globalize3](https://github.com/globalize/globalize) may help you implement it. +NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](https://groups.google.com/forum/#!topic/rails-i18n/FN7eLH2-lHA)), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Few gems such as [Globalize3](https://github.com/globalize/globalize) may help you implement it. The **translations load path** (`I18n.load_path`) is an array of paths to files that will be loaded automatically. Configuring this path allows for customization of translations directory structure and file naming scheme. @@ -313,7 +313,7 @@ In general, this approach is far less reliable than using the language header an WARNING: You may be tempted to store the chosen locale in a _session_ or a *cookie*. However, **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [*RESTful*](https://en.wikipedia.org/wiki/Representational_State_Transfer). Read more about the RESTful approach in [Stefan Tilkov's articles](https://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below. Internationalization and Localization ------------------------------------ +------------------------------------- OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. @@ -371,7 +371,7 @@ end ```html+erb # app/views/home/index.html.erb -<h1><%=t :hello_world %></h1> +<h1><%= t :hello_world %></h1> <p><%= flash[:notice] %></p> ``` @@ -416,7 +416,7 @@ If your translations are stored in YAML files, certain keys must be escaped. The Examples: -```erb +```yaml # config/locales/en.yml en: success: @@ -430,12 +430,12 @@ en: ``` ```ruby -I18n.t 'success.true' # => 'True!' -I18n.t 'success.on' # => 'On!' +I18n.t 'success.true' # => 'True!' +I18n.t 'success.on' # => 'On!' I18n.t 'success.false' # => 'False!' I18n.t 'failure.false' # => Translation Missing -I18n.t 'failure.off' # => Translation Missing -I18n.t 'failure.true' # => Translation Missing +I18n.t 'failure.off' # => Translation Missing +I18n.t 'failure.true' # => Translation Missing ``` ### Passing Variables to Translations @@ -502,7 +502,7 @@ OK! Now let's add a timestamp to the view, so we can demo the **date/time locali ```erb # app/views/home/index.html.erb -<h1><%=t :hello_world %></h1> +<h1><%= t :hello_world %></h1> <p><%= flash[:notice] %></p> <p><%= l Time.now, format: :short %></p> ``` @@ -1050,7 +1050,7 @@ The Simple backend shipped with Active Support allows you to store translations For example a Ruby Hash providing translations can look like this: -```yaml +```ruby { pt: { foo: { @@ -1187,7 +1187,7 @@ If you find your own locale (language) missing from our [example translations da Resources --------- -* [Google group: rails-i18n](http://groups.google.com/group/rails-i18n) - The project's mailing list. +* [Google group: rails-i18n](https://groups.google.com/forum/#!forum/rails-i18n) - The project's mailing list. * [GitHub: rails-i18n](https://github.com/svenfuchs/rails-i18n) - Code repository and issue tracker for the rails-i18n project. Most importantly you can find lots of [example translations](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for Rails that should work for your application in most cases. * [GitHub: i18n](https://github.com/svenfuchs/i18n) - Code repository and issue tracker for the i18n gem. diff --git a/guides/source/kindle/toc.html.erb b/guides/source/kindle/toc.html.erb index f310edd3a1..0f4228ed6b 100644 --- a/guides/source/kindle/toc.html.erb +++ b/guides/source/kindle/toc.html.erb @@ -14,7 +14,7 @@ Ruby on Rails Guides <% if document['work_in_progress']%>(WIP)<% end %> </li> <% end %> - </ul> + </ul> <% end %> <hr /> <ul> diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 6bce73ccad..27cef2bd27 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -24,11 +24,11 @@ In order to understand Ajax, you must first understand what a web browser does normally. When you type `http://localhost:3000` into your browser's address bar and hit -'Go,' the browser (your 'client') makes a request to the server. It parses the +'Go', the browser (your 'client') makes a request to the server. It parses the response, then fetches all associated assets, like JavaScript files, stylesheets and images. It then assembles the page. If you click a link, it does the same process: fetch the page, fetch the assets, put it all together, -show you the results. This is called the 'request response cycle.' +show you the results. This is called the 'request response cycle'. JavaScript can also make requests to the server, and parse the response. It also has the ability to update information on the page. Combining these two @@ -57,7 +57,7 @@ will show you how Rails can help you write websites in this way, but it's all built on top of this fairly simple technique. Unobtrusive JavaScript -------------------------------------- +---------------------- Rails uses a technique called "Unobtrusive JavaScript" to handle attaching JavaScript to the DOM. This is generally considered to be a best-practice @@ -139,7 +139,7 @@ JavaScript) in this style, and you can expect that many libraries will also follow this pattern. Built-in Helpers ----------------------- +---------------- ### Remote elements @@ -372,9 +372,9 @@ is also useful for manipulating form data before serialization. The `ajax:beforeSend` event is also useful for adding custom request headers. If you stop the `ajax:aborted:file` event, the default behavior of allowing the -browser to submit the form via normal means (i.e. non-AJAX submission) will be +browser to submit the form via normal means (i.e. non-Ajax submission) will be canceled and the form will not be submitted at all. This is useful for -implementing your own AJAX file upload workaround. +implementing your own Ajax file upload workaround. ### Rails-ujs event handlers diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc index 6b9a243593..c70a9f0ba0 100644 --- a/railties/RDOC_MAIN.rdoc +++ b/railties/RDOC_MAIN.rdoc @@ -1,35 +1,50 @@ == Welcome to \Rails -\Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the {Model-View-Controller (MVC)}[https://en.wikipedia.org/wiki/Model-view-controller] pattern. - -Understanding the MVC pattern is key to understanding \Rails. MVC divides your application -into three layers, each with a specific responsibility. - -The View layer is composed of "templates" that are responsible for providing -appropriate representations of your application's resources. Templates -can come in a variety of formats, but most view templates are \HTML with embedded Ruby -code (.erb files). - -The Model layer represents your domain model (such as Account, Product, Person, Post) -and encapsulates the business logic that is specific to your application. In \Rails, -database-backed model classes are derived from ActiveRecord::Base. Active Record allows -you to present the data from database rows as objects and embellish these data objects -with business logic methods. Although most \Rails models are backed by a database, models -can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as -provided by the ActiveModel module. You can read more about Active Record in its -{README}[link:files/activerecord/README_rdoc.html]. - -The Controller layer is responsible for handling incoming HTTP requests and providing a -suitable response. Usually this means returning \HTML, but \Rails controllers can also -generate XML, JSON, PDFs, mobile-specific views, and more. Controllers manipulate models -and render view templates in order to generate the appropriate HTTP response. - -In \Rails, the Controller and View layers are handled together by Action Pack. -These two layers are bundled in a single package due to their heavy interdependence. -This is unlike the relationship between Active Record and Action Pack, which are -independent. Each of these packages can be used independently outside of \Rails. You -can read more about Action Pack in its {README}[link:files/actionpack/README_rdoc.html]. +\Rails is a web-application framework that includes everything needed to +create database-backed web applications according to the +{Model-View-Controller (MVC)}[http://en.wikipedia.org/wiki/Model-view-controller] +pattern. + +Understanding the MVC pattern is key to understanding \Rails. MVC divides your +application into three layers, each with a specific responsibility. + +The <em>Model layer</em> represents your domain model (such as Account, Product, +Person, Post, etc.) and encapsulates the business logic that is specific to +your application. In \Rails, database-backed model classes are derived from +ActiveRecord::Base. Active Record allows you to present the data from +database rows as objects and embellish these data objects with business logic +methods. You can read more about Active Record in its {README}[link:files/activerecord/README_rdoc.html]. +Although most \Rails models are backed by a database, models can also be ordinary +Ruby classes, or Ruby classes that implement a set of interfaces as provided by +the Active Model module. You can read more about Active Model in its {README}[link:files/activemodel/README_rdoc.html]. + +The <em>Controller layer</em> is responsible for handling incoming HTTP requests and +providing a suitable response. Usually this means returning \HTML, but \Rails controllers +can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers load and +manipulate models, and render view templates in order to generate the appropriate HTTP response. +In \Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and +controller classes are derived from ActionController::Base. Action Dispatch and Action Controller +are bundled together in Action Pack. You can read more about Action Pack in its +{README}[link:files/actionpack/README_rdoc.html]. + +The <em>View layer</em> is composed of "templates" that are responsible for providing +appropriate representations of your application's resources. Templates can +come in a variety of formats, but most view templates are \HTML with embedded +Ruby code (ERB files). Views are typically rendered to generate a controller response, +or to generate the body of an email. In \Rails, View generation is handled by Action View. +You can read more about Action View in its {README}[link:files/actionview/README_rdoc.html]. + +Active Record, Active Model, Action Pack, and Action View can each be used independently outside \Rails. +In addition to that, \Rails also comes with Action Mailer ({README}[link:files/actionmailer/README_rdoc.html]), a library +to generate and send emails; Active Job ({README}[link:files/activejob/README_md.html]), a +framework for declaring jobs and making them run on a variety of queueing +backends; Action Cable ({README}[link:files/actioncable/README_md.html]), a framework to +integrate WebSockets with a \Rails application; +Active Storage ({README}[link:files/activestorage/README_md.html]), a library to attach cloud +and local files to \Rails applications; +and Active Support ({README}[link:files/activesupport/README_rdoc.html]), a collection +of utility classes and standard library extensions that are useful for \Rails, +and may also be used independently outside \Rails. == Getting Started @@ -45,28 +60,31 @@ can read more about Action Pack in its {README}[link:files/actionpack/README_rdo 3. Change directory to +myapp+ and start the web server: - $ cd myapp; rails server + $ cd myapp + $ rails server Run with <tt>--help</tt> or <tt>-h</tt> for options. -4. Go to http://localhost:3000 and you'll see: - - "Yay! You’re on Rails!" +4. Go to <tt>http://localhost:3000</tt> and you'll see: "Yay! You’re on \Rails!" 5. Follow the guidelines to start developing your application. You may find the following resources handy: -* The \README file created within your application. -* {Getting Started with \Rails}[http://guides.rubyonrails.org/getting_started.html]. -* {Ruby on \Rails Tutorial}[https://www.railstutorial.org/book]. -* {Ruby on \Rails Guides}[http://guides.rubyonrails.org]. -* {The API Documentation}[http://api.rubyonrails.org]. + * The \README file created within your application. + * {Getting Started with \Rails}[http://guides.rubyonrails.org/getting_started.html]. + * {Ruby on \Rails Guides}[http://guides.rubyonrails.org]. + * {The API Documentation}[http://api.rubyonrails.org]. + * {Ruby on \Rails Tutorial}[https://www.railstutorial.org/book]. == Contributing -We encourage you to contribute to Ruby on \Rails! Please check out the {Contributing to Rails -guide}[http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how -to proceed. {Join us}[http://contributors.rubyonrails.org]! +We encourage you to contribute to Ruby on \Rails! Please check out the +{Contributing to Ruby on \Rails guide}[http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how to proceed. {Join us!}[http://contributors.rubyonrails.org] + +Trying to report a possible security vulnerability in \Rails? Please +check out our {security policy}[http://rubyonrails.org/security/] for +guidelines about how to proceed. +Everyone interacting in \Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the \Rails {code of conduct}[http://rubyonrails.org/conduct/]. == License diff --git a/railties/lib/rails/api/generator.rb b/railties/lib/rails/api/generator.rb index 6e5eec2e34..3405560b74 100644 --- a/railties/lib/rails/api/generator.rb +++ b/railties/lib/rails/api/generator.rb @@ -6,8 +6,11 @@ class RDoc::Generator::API < RDoc::Generator::SDoc # :nodoc: RDoc::RDoc.add_generator self def generate_class_tree_level(classes, visited = {}) - # Only process core extensions on the first visit. + # Only process core extensions on the first visit and remove + # Active Storage duplicated classes that are at the top level + # since they aren't nested under a definition of the `ActiveStorage` module. if visited.empty? + classes = classes.reject { |klass| active_storage?(klass) } core_exts, classes = classes.partition { |klass| core_extension?(klass) } super.unshift([ "Core extensions", "", "", build_core_ext_subtree(core_exts, visited) ]) @@ -27,4 +30,8 @@ class RDoc::Generator::API < RDoc::Generator::SDoc # :nodoc: def core_extension?(klass) klass.name != "ActiveSupport" && klass.in_files.any? { |file| file.absolute_name.include?("core_ext") } end + + def active_storage?(klass) + klass.name != "ActiveStorage" && klass.in_files.all? { |file| file.absolute_name.include?("active_storage") } + end end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 31e2a45bff..b65289177f 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -151,7 +151,7 @@ module Rails end # Loads and returns the entire raw configuration of database from - # values stored in `config/database.yml`. + # values stored in <tt>config/database.yml</tt>. def database_configuration path = paths["config/database"].existent.first yaml = Pathname.new(path) if path diff --git a/railties/lib/rails/command/actions.rb b/railties/lib/rails/command/actions.rb index 2f6827b7f4..cbb743346b 100644 --- a/railties/lib/rails/command/actions.rb +++ b/railties/lib/rails/command/actions.rb @@ -3,9 +3,9 @@ module Rails module Command module Actions - # Change to the application's path if there is no config.ru file in current directory. - # This allows us to run `rails server` from other directories, but still get - # the main config.ru and properly set the tmp directory. + # Change to the application's path if there is no <tt>config.ru</tt> file in current directory. + # This allows us to run <tt>rails server</tt> from other directories, but still get + # the main <tt>config.ru</tt> and properly set the <tt>tmp</tt> directory. def set_application_directory! Dir.chdir(File.expand_path("../..", APP_PATH)) unless File.exist?(File.expand_path("config.ru")) end diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb index e92b2042bd..8df4f98d3e 100644 --- a/railties/lib/rails/command/base.rb +++ b/railties/lib/rails/command/base.rb @@ -112,8 +112,8 @@ module Rails # Default file root to place extra files a command might need, placed # one folder above the command file. # - # For a `Rails::Command::TestCommand` placed in `rails/command/test_command.rb` - # would return `rails/test`. + # For a Rails::Command::TestCommand placed in <tt>rails/command/test_command.rb</tt> + # would return <tt>rails/test</tt>. def default_command_root path = File.expand_path(File.join("../commands", command_root_namespace), __dir__) path if File.exist?(path) diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb index 6296c95a87..5234969743 100644 --- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb +++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb @@ -75,7 +75,7 @@ module Rails args += ["-P", "#{config['password']}"] if config["password"] if config["host"] - host_arg = "#{config['host']}" + host_arg = "#{config['host']}".dup host_arg << ":#{config['port']}" if config["port"] args += ["-S", host_arg] end diff --git a/railties/lib/rails/secrets.rb b/railties/lib/rails/secrets.rb index 33821dd759..aea72b2d01 100644 --- a/railties/lib/rails/secrets.rb +++ b/railties/lib/rails/secrets.rb @@ -44,7 +44,7 @@ module Rails <<-end_of_template.strip_heredoc # See `secrets.yml` for tips on generating suitable keys. # production: - # external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289 + # external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289 end_of_template end diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb index e922a23e15..6ad96b28c7 100644 --- a/railties/test/commands/dbconsole_test.rb +++ b/railties/test/commands/dbconsole_test.rb @@ -206,6 +206,12 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase assert_equal ["sqlplus", "user/secret@db"], dbconsole.find_cmd_and_exec_args end + def test_sqlserver + start(adapter: "sqlserver", database: "db", username: "user", password: "secret", host: "localhost", port: 1433) + assert_not aborted + assert_equal ["sqsh", "-D", "db", "-U", "user", "-P", "secret", "-S", "localhost:1433"], dbconsole.find_cmd_and_exec_args + end + def test_unknown_command_line_client start(adapter: "unknown", database: "db") assert aborted diff --git a/railties/test/generators/encrypted_secrets_generator_test.rb b/railties/test/generators/encrypted_secrets_generator_test.rb index 205827e498..eacb5166c0 100644 --- a/railties/test/generators/encrypted_secrets_generator_test.rb +++ b/railties/test/generators/encrypted_secrets_generator_test.rb @@ -17,8 +17,8 @@ class EncryptedSecretsGeneratorTest < Rails::Generators::TestCase assert_file "config/secrets.yml.key", /\w+/ assert File.exist?("config/secrets.yml.enc") - assert_no_match(/production:\n# external_api_key: \w+/, IO.binread("config/secrets.yml.enc")) - assert_match(/production:\n# external_api_key: \w+/, Rails::Secrets.read) + assert_no_match(/# production:\n# external_api_key: \w+/, IO.binread("config/secrets.yml.enc")) + assert_match(/# production:\n# external_api_key: \w+/, Rails::Secrets.read) end def test_appends_to_gitignore diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb index 445e216f16..ff6f955a78 100644 --- a/railties/test/secrets_test.rb +++ b/railties/test/secrets_test.rb @@ -47,7 +47,7 @@ class Rails::SecretsTest < ActiveSupport::TestCase ENV["RAILS_MASTER_KEY"] = IO.binread("config/secrets.yml.key").strip FileUtils.rm("config/secrets.yml.key") - assert_match "production:\n# external_api_key", Rails::Secrets.read + assert_match "# production:\n# external_api_key:", Rails::Secrets.read ensure ENV["RAILS_MASTER_KEY"] = old_key end @@ -69,7 +69,7 @@ class Rails::SecretsTest < ActiveSupport::TestCase Rails::Secrets.read_for_editing do |tmp_path| decrypted_path = tmp_path - assert_match(/production:\n# external_api_key/, File.read(tmp_path)) + assert_match(/# production:\n# external_api_key/, File.read(tmp_path)) File.write(tmp_path, "Empty streets, empty nights. The Downtown Lights.") end |