Merge branch 'main' into use-precision-in-updated-at-upsert

This commit is contained in:
Aaron Patterson 2021-09-14 12:20:09 -07:00 committed by GitHub
commit 6324abe5c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
149 changed files with 2466 additions and 2220 deletions

1
.gitignore vendored
View File

@ -2,7 +2,6 @@
# Check out https://help.github.com/articles/ignoring-files for how to set that up.
.Gemfile
.byebug_history
.ruby-version
/*/doc/
/*/test/tmp/

View File

@ -143,6 +143,9 @@ Style/DefWithParentheses:
Style/MethodDefParentheses:
Enabled: true
Style/ExplicitBlockArgument:
Enabled: true
Style/FrozenStringLiteralComment:
Enabled: true
EnforcedStyle: always

View File

@ -15,8 +15,10 @@ gem "selenium-webdriver", ">= 4.0.0.alpha7"
gem "rack-cache", "~> 1.2"
gem "stimulus-rails"
gem "turbo-rails"
gem "webpacker", "~> 6.0.0.rc.5", require: ENV["SKIP_REQUIRE_WEBPACKER"] != "true"
gem "jsbundling-rails"
gem "cssbundling-rails"
gem "importmap-rails"
gem "tailwindcss-rails"
# require: false so bcrypt is loaded only when has_secure_password is used.
# This is to avoid Active Model (and by extension the entire framework)
# being dependent on a binary library.
@ -117,7 +119,7 @@ group :test do
platforms :mri do
gem "stackprof"
gem "byebug"
gem "debug", ">= 1.0.0", require: false
end
gem "benchmark-ips"

View File

@ -81,7 +81,6 @@ PATH
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.5.0.beta2)
rails (7.0.0.alpha)
actioncable (= 7.0.0.alpha)
actionmailbox (= 7.0.0.alpha)
@ -103,6 +102,7 @@ PATH
method_source
rake (>= 0.13)
thor (~> 1.0)
zeitwerk (~> 2.5.0.beta3)
GEM
remote: https://rubygems.org/
@ -164,7 +164,6 @@ GEM
bunny (2.18.0)
amq-protocol (~> 2.3, >= 2.3.1)
sorted_set (~> 1, >= 1.0.2)
byebug (11.1.3)
capybara (3.35.3)
addressable
mini_mime (>= 0.1.3)
@ -184,10 +183,15 @@ GEM
crack (0.4.5)
rexml
crass (1.0.6)
cssbundling-rails (0.1.0)
rails (>= 6.0.0)
curses (1.4.2)
daemons (1.4.0)
dalli (2.7.11)
dante (0.2.0)
debug (1.0.0)
irb
reline (>= 0.2.7)
declarative (0.0.20)
delayed_job (4.1.9)
activesupport (>= 3.0, < 6.2)
@ -286,9 +290,14 @@ GEM
image_processing (1.12.1)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
importmap-rails (0.3.4)
importmap-rails (0.5.1)
rails (>= 6.0.0)
io-console (0.5.9)
irb (1.3.7)
reline (>= 0.2.7)
jmespath (1.4.0)
jsbundling-rails (0.1.0)
rails (>= 6.0.0)
json (2.5.1)
jwt (2.2.3)
kindlerb (1.2.0)
@ -354,8 +363,6 @@ GEM
rack (>= 0.4)
rack-protection (2.1.0)
rack
rack-proxy (0.7.0)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails-dom-testing (2.0.3)
@ -375,6 +382,8 @@ GEM
redis-namespace (1.8.1)
redis (>= 3.0.4)
regexp_parser (2.1.1)
reline (0.2.7)
io-console (~> 0.5)
representable (3.1.1)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
@ -428,7 +437,6 @@ GEM
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2)
rubyzip (>= 1.2.2)
semantic_range (3.0.0)
sequel (5.45.0)
serverengine (2.0.7)
sigdump (~> 0.2.2)
@ -467,10 +475,12 @@ GEM
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
stackprof (0.2.17)
stimulus-rails (0.3.9)
stimulus-rails (0.4.2)
rails (>= 6.0.0)
sucker_punch (3.0.1)
concurrent-ruby (~> 1.0)
tailwindcss-rails (0.4.3)
rails (>= 6.0.0)
terser (1.1.4)
execjs (>= 0.3.0, < 3)
thin (1.8.1)
@ -480,7 +490,7 @@ GEM
thor (1.1.0)
tilt (2.0.10)
trailblazer-option (0.1.1)
turbo-rails (0.7.4)
turbo-rails (0.7.11)
rails (>= 6.0.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
@ -501,11 +511,6 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (6.0.0.rc.5)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webrick (1.7.0)
websocket (1.2.9)
websocket-driver (0.7.5)
@ -513,7 +518,7 @@ GEM
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.5.0.beta2)
zeitwerk (2.5.0.beta3)
PLATFORMS
ruby
@ -533,16 +538,18 @@ DEPENDENCIES
benchmark-ips
blade
bootsnap (>= 1.4.4)
byebug
capybara (>= 3.26)
connection_pool
cssbundling-rails
dalli
debug (>= 1.0.0)
delayed_job
delayed_job_active_record
google-cloud-storage (~> 1.11)
hiredis
image_processing (~> 1.2)
importmap-rails
jsbundling-rails
json (>= 2.0.0)
kindlerb (~> 1.2.0)
libxml-ruby
@ -583,6 +590,7 @@ DEPENDENCIES
stackprof
stimulus-rails
sucker_punch
tailwindcss-rails
terser (>= 1.1.4)
turbo-rails
tzinfo-data
@ -590,7 +598,6 @@ DEPENDENCIES
wdm (>= 0.1.0)
webdrivers
webmock
webpacker (~> 6.0.0.rc.5)
webrick
websocket-client-simple!

View File

@ -49,12 +49,12 @@ give them a heads up that Rails will be released soonish.
This is only required for major and minor releases, bugfix releases aren't a
big enough deal, and are supposed to be backward compatible.
Send an email just giving a heads up about the upcoming release to these
Send a message just giving a heads up about the upcoming release to these
lists:
* team@jruby.org
* community@rubini.us
* rubyonrails-core@googlegroups.com
* [rubyonrails-core](https://discuss.rubyonrails.org/c/rubyonrails-core)
Implementors will love you and help you.
@ -135,8 +135,8 @@ Write a release announcement that includes the version, changes, and links to
GitHub where people can find the specific commit list. Here are the mailing
lists where you should announce:
* rubyonrails-core@googlegroups.com
* rubyonrails-talk@googlegroups.com
* [rubyonrails-core](https://discuss.rubyonrails.org/c/rubyonrails-core)
* [rubyonrails-talk](https://discuss.rubyonrails.org/c/rubyonrails-talk)
* ruby-talk@ruby-lang.org
Use Markdown format for your announcement. Remember to ask people to report

View File

@ -18,10 +18,10 @@ module ActionCable
@tags = @tags.uniq
end
def tag(logger)
def tag(logger, &block)
if logger.respond_to?(:tagged)
current_tags = tags - logger.formatter.current_tags
logger.tagged(*current_tags) { yield }
logger.tagged(*current_tags, &block)
else
yield
end

View File

@ -36,12 +36,10 @@ module ActionCable
@executor.shuttingdown?
end
def work(connection)
def work(connection, &block)
self.connection = connection
run_callbacks :work do
yield
end
run_callbacks :work, &block
ensure
self.connection = nil
end

View File

@ -12,8 +12,8 @@ module ActionCable
end
end
def with_database_connections
connection.logger.tag(ActiveRecord::Base.logger) { yield }
def with_database_connections(&block)
connection.logger.tag(ActiveRecord::Base.logger, &block)
end
end
end

View File

@ -13,51 +13,98 @@ module Rails
hook_for :test_framework
def create_channel_file
template "channel.rb", File.join("app/channels", class_path, "#{file_name}_channel.rb")
def create_channel_files
create_shared_channel_files
create_channel_file
if options[:assets]
if behavior == :invoke
if defined?(Webpacker::Engine)
template "javascript/index.js", "#{Webpacker.config.source_path}/channels/index.js"
template "javascript/consumer.js", "#{Webpacker.config.source_path}/channels/consumer.js"
else
template "javascript/consumer.js", "app/javascript/channels/consumer.js"
if using_javascript?
if first_setup_required?
create_shared_channel_javascript_files
import_channels_in_javascript_entrypoint
if using_importmap?
pin_javascript_dependencies
elsif using_node?
install_javascript_dependencies
end
end
if defined?(Webpacker::Engine)
js_template "javascript/channel", File.join(Webpacker.config.source_path, "channels", class_path, "#{file_name}_channel")
else
channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel")
js_template "javascript/channel", channel_js_path
gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer"
append_to_file "app/javascript/application.js", %(\nimport "channels/#{file_name}_channel"\n)
end
create_channel_javascript_file
import_channel_in_javascript_entrypoint
end
generate_application_cable_files
end
private
def create_shared_channel_files
return if behavior != :invoke
copy_file "#{__dir__}/templates/application_cable/channel.rb",
"app/channels/application_cable/channel.rb"
copy_file "#{__dir__}/templates/application_cable/connection.rb",
"app/channels/application_cable/connection.rb"
end
def create_channel_file
template "channel.rb",
File.join("app/channels", class_path, "#{file_name}_channel.rb")
end
def create_shared_channel_javascript_files
template "javascript/index.js", "app/javascript/channels/index.js"
template "javascript/consumer.js", "app/javascript/channels/consumer.js"
end
def create_channel_javascript_file
channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel")
js_template "javascript/channel", channel_js_path
gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer" unless using_node?
end
def import_channels_in_javascript_entrypoint
append_to_file "app/javascript/application.js",
using_node? ? %(import "./channels"\n) : %(import "channels"\n)
end
def import_channel_in_javascript_entrypoint
append_to_file "app/javascript/channels/index.js",
using_node? ? %(import "./#{file_name}_channel"\n) : %(import "channels/#{file_name}_channel"\n)
end
def install_javascript_dependencies
say "Installing JavaScript dependencies", :green
run "yarn add @rails/actioncable"
end
def pin_javascript_dependencies
append_to_file "config/importmap.rb", <<-RUBY
pin "@rails/actioncable", to: "actioncable.esm.js"
pin_all_from "app/javascript/channels", under: "channels"
RUBY
end
def file_name
@_file_name ||= super.sub(/_channel\z/i, "")
end
# FIXME: Change these files to symlinks once RubyGems 2.5.0 is required.
def generate_application_cable_files
return if behavior != :invoke
def first_setup_required?
!root.join("app/javascript/channels/index.js").exist?
end
files = [
"application_cable/channel.rb",
"application_cable/connection.rb"
]
def using_javascript?
@using_javascript ||= options[:assets] && root.join("app/javascript").exist?
end
files.each do |name|
path = File.join("app/channels/", name)
template(name, path) if !File.exist?(path)
end
def using_node?
@using_node ||= root.join("package.json").exist?
end
def using_importmap?
@using_importmap ||= root.join("config/importmap.rb").exist?
end
def root
@root ||= Pathname(destination_root)
end
end
end

View File

@ -1,5 +1 @@
// Load all the channels within this directory and all subdirectories.
// Channel files must be named *_channel.js.
const channels = require.context('.', true, /_channel\.js$/)
channels.keys().forEach(channels)
// Import all the channels to be used by Action Cable

View File

@ -7,11 +7,6 @@ require "active_support/testing/method_call_assertions"
require "puma"
require "rack/mock"
begin
require "byebug"
rescue LoadError
end
# Require all the stubs and models
Dir[File.expand_path("stubs/*.rb", __dir__)].each { |file| require file }

View File

@ -7,7 +7,6 @@ require_relative "../test/dummy/config/environment"
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
require "rails/test_help"
require "byebug"
require "webmock/minitest"
require "rails/test_unit/reporter"

View File

@ -1,3 +1,11 @@
* Use a static error message when raising `ActionDispatch::Http::Parameters::ParseError`
to avoid inadvertently logging the HTTP request body at the `fatal` level when it contains
malformed JSON.
Fixes #41145
*Aaron Lahey*
* Add `Middleware#delete!` to delete middleware or raise if not found.
`Middleware#delete!` works just like `Middleware#delete` but will

View File

@ -142,8 +142,8 @@ module AbstractController
end
end
def instrument_fragment_cache(name, key) # :nodoc:
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
def instrument_fragment_cache(name, key, &block) # :nodoc:
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key), &block)
end
end
end

View File

@ -940,7 +940,7 @@ module ActionController
def each_element(object, &block)
case object
when Array
object.grep(Parameters).filter_map { |el| yield el }
object.grep(Parameters).filter_map(&block)
when Parameters
if object.nested_attributes?
object.each_nested_attribute(&block)

View File

@ -13,8 +13,8 @@ module Mime
@symbols = []
end
def each
@mimes.each { |x| yield x }
def each(&block)
@mimes.each(&block)
end
def <<(type)
@ -42,9 +42,9 @@ module Mime
Type.lookup_by_extension(type)
end
def fetch(type)
def fetch(type, &block)
return type if type.is_a?(Type)
EXTENSION_LOOKUP.fetch(type.to_s) { |k| yield k }
EXTENSION_LOOKUP.fetch(type.to_s, &block)
end
end

View File

@ -17,8 +17,8 @@ module ActionDispatch
# Raised when raw data from the request cannot be parsed by the parser
# defined for request's content MIME type.
class ParseError < StandardError
def initialize
super($!.message)
def initialize(message = $!.message)
super(message)
end
end
@ -93,7 +93,7 @@ module ActionDispatch
strategy.call(raw_post)
rescue # JSON or Ruby code block errors.
log_parse_error_once
raise ParseError
raise ParseError, "Error occurred while parsing request parameters"
end
end

View File

@ -270,9 +270,10 @@ module ActionDispatch
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
# req.standard_port # => 80
def standard_port
case protocol
when "https://" then 443
else 80
if "https://" == protocol
443
else
80
end
end

View File

@ -83,8 +83,8 @@ module ActionDispatch
yield(self) if block_given?
end
def each
@middlewares.each { |x| yield x }
def each(&block)
@middlewares.each(&block)
end
def size

View File

@ -922,7 +922,7 @@ module ActionDispatch
# namespace :admin, as: "sekret" do
# resources :posts
# end
def namespace(path, options = {})
def namespace(path, options = {}, &block)
path = path.to_s
defaults = {
@ -933,7 +933,7 @@ module ActionDispatch
}
path_scope(options.delete(:path) { path }) do
scope(defaults.merge!(options)) { yield }
scope(defaults.merge!(options), &block)
end
end
@ -992,8 +992,8 @@ module ActionDispatch
# constraints(Iphone) do
# resources :iphones
# end
def constraints(constraints = {})
scope(constraints: constraints) { yield }
def constraints(constraints = {}, &block)
scope(constraints: constraints, &block)
end
# Allows you to set default parameters for a route, such as this:
@ -1493,15 +1493,13 @@ module ActionDispatch
# with GET, and route to the search action of +PhotosController+. It will also
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
# route helpers.
def collection
def collection(&block)
unless resource_scope?
raise ArgumentError, "can't use collection outside resource(s) scope"
end
with_scope_level(:collection) do
path_scope(parent_resource.collection_scope) do
yield
end
path_scope(parent_resource.collection_scope, &block)
end
end
@ -1516,7 +1514,7 @@ module ActionDispatch
# This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
# preview action of +PhotosController+. It will also create the
# <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
def member
def member(&block)
unless resource_scope?
raise ArgumentError, "can't use member outside resource(s) scope"
end
@ -1524,27 +1522,25 @@ module ActionDispatch
with_scope_level(:member) do
if shallow?
shallow_scope {
path_scope(parent_resource.member_scope) { yield }
path_scope(parent_resource.member_scope, &block)
}
else
path_scope(parent_resource.member_scope) { yield }
path_scope(parent_resource.member_scope, &block)
end
end
end
def new
def new(&block)
unless resource_scope?
raise ArgumentError, "can't use new outside resource(s) scope"
end
with_scope_level(:new) do
path_scope(parent_resource.new_scope(action_path(:new))) do
yield
end
path_scope(parent_resource.new_scope(action_path(:new)), &block)
end
end
def nested
def nested(&block)
unless resource_scope?
raise ArgumentError, "can't use nested outside resource(s) scope"
end
@ -1553,12 +1549,12 @@ module ActionDispatch
if shallow? && shallow_nesting_depth >= 1
shallow_scope do
path_scope(parent_resource.nested_scope) do
scope(nested_options) { yield }
scope(nested_options, &block)
end
end
else
path_scope(parent_resource.nested_scope) do
scope(nested_options) { yield }
scope(nested_options, &block)
end
end
end
@ -1744,10 +1740,10 @@ module ActionDispatch
@scope = @scope.parent
end
def resource_scope(resource)
def resource_scope(resource, &block)
@scope = @scope.new(scope_level_resource: resource)
controller(resource.resource_scope) { yield }
controller(resource.resource_scope, &block)
ensure
@scope = @scope.parent
end
@ -1865,7 +1861,7 @@ module ActionDispatch
end
def map_match(paths, options)
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
if (on = options[:on]) && !VALID_ON_OPTIONS.include?(on)
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
end

View File

@ -131,8 +131,8 @@ module ActionDispatch
alias [] get
alias clear clear!
def each
routes.each { |name, route| yield name, route }
def each(&block)
routes.each(&block)
self
end

View File

@ -30,13 +30,16 @@ module ActionDispatch
EOM
elsif path.exist?
non_deprecated_path = Pathname(File.absolute_path(path)).relative_path_from(Pathname(File.absolute_path(self.class.file_fixture_path)))
ActiveSupport::Deprecation.warn(<<~EOM)
Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
In Rails 7.0, the path needs to be relative to `file_fixture_path`.
Please modify the call from
`fixture_file_upload("#{original_path}")` to `fixture_file_upload("#{non_deprecated_path}")`.
EOM
if Pathname(original_path) != non_deprecated_path
ActiveSupport::Deprecation.warn(<<~EOM)
Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
In Rails 7.0, the path needs to be relative to `file_fixture_path`.
Please modify the call from
`fixture_file_upload("#{original_path}")` to `fixture_file_upload("#{non_deprecated_path}")`.
EOM
end
else
path = file_fixture(original_path)
end

View File

@ -314,8 +314,8 @@ module ActionController
def setup
super
def @controller.new_controller_thread
Thread.new { yield }
def @controller.new_controller_thread(&block)
Thread.new(&block)
end
end

View File

@ -987,8 +987,8 @@ class LiveHeadRenderTest < ActionController::TestCase
def setup
super
def @controller.new_controller_thread
Thread.new { yield }
def @controller.new_controller_thread(&block)
Thread.new(&block)
end
def @controller.response_body=(body)

View File

@ -634,14 +634,12 @@ module RequestForgeryProtectionTests
assert_response :success
end
def assert_cross_origin_blocked
assert_raises(ActionController::InvalidCrossOriginRequest) do
yield
end
def assert_cross_origin_blocked(&block)
assert_raises(ActionController::InvalidCrossOriginRequest, &block)
end
def assert_cross_origin_not_blocked
assert_not_blocked { yield }
def assert_cross_origin_not_blocked(&block)
assert_not_blocked(&block)
end
def forgery_protection_origin_check
@ -701,10 +699,8 @@ end
class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::TestCase
include RequestForgeryProtectionTests
def assert_blocked
assert_raises(ActionController::InvalidAuthenticityToken) do
yield
end
def assert_blocked(&block)
assert_raises(ActionController::InvalidAuthenticityToken, &block)
end
def test_raised_exception_message_explains_why_it_occurred
@ -1105,10 +1101,8 @@ class SkipProtectionControllerTest < ActionController::TestCase
assert_not_blocked { post :index }
end
def assert_blocked
assert_raises(ActionController::InvalidAuthenticityToken) do
yield
end
def assert_blocked(&block)
assert_raises(ActionController::InvalidAuthenticityToken, &block)
end
def assert_not_blocked(&block)

View File

@ -973,6 +973,16 @@ XML
end
end
def test_fixture_file_upload_fixture_path_same_as_file_fixture_path
TestCaseTest.stub :fixture_path, File.expand_path("../fixtures/multipart", __dir__) do
TestCaseTest.stub :file_fixture_path, File.expand_path("../fixtures/multipart", __dir__) do
assert_not_deprecated do
fixture_file_upload("ruby_on_rails.jpg", "image/jpg")
end
end
end
end
def test_fixture_file_upload_ignores_fixture_path_given_full_path
TestCaseTest.stub :fixture_path, __dir__ do
uploaded_file = fixture_file_upload("#{FILES_DIR}/ruby_on_rails.jpg", "image/jpeg")

View File

@ -193,6 +193,16 @@ module ActionDispatch
end
end
def test_raising_error_when_invalid_on_option_is_given
fakeset = FakeSet.new
mapper = Mapper.new fakeset
error = assert_raise ArgumentError do
mapper.get "/foo", on: :invalid_option
end
assert_equal "Unknown scope :invalid_option given to :on", error.message
end
def test_scope_does_not_destructively_mutate_default_options
fakeset = FakeSet.new
mapper = Mapper.new fakeset

View File

@ -80,7 +80,7 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
post "/parse", params: json, headers: { "CONTENT_TYPE" => "application/json", "action_dispatch.show_exceptions" => false }
end
assert_equal JSON::ParserError, exception.cause.class
assert_equal exception.cause.message, exception.message
assert_equal "Error occurred while parsing request parameters", exception.message
ensure
$stderr = STDERR
end

View File

@ -9,70 +9,42 @@ module ActionText
source_root File.expand_path("templates", __dir__)
def install_javascript_dependencies
if defined?(Webpacker::Engine)
if Pathname(destination_root).join("package.json").exist?
say "Installing JavaScript dependencies", :green
yarn_command "add #{js_dependencies.map { |name, version| "#{name}@#{version}" }.join(" ")}"
run "yarn add @rails/actiontext trix"
end
end
def append_javascript_dependencies
if defined?(Webpacker::Engine)
if (app_javascript_pack_path = Pathname.new("#{Webpacker.config.source_entry_path}/application.js")).exist?
js_dependencies.each_key do |dependency|
line = %[import "#{dependency}"]
destination = Pathname(destination_root)
unless app_javascript_pack_path.read.include? line
say "Adding #{dependency} to #{app_javascript_pack_path}", :green
append_to_file app_javascript_pack_path, "\n#{line}"
end
end
else
say <<~WARNING, :red
WARNING: Action Text can't locate your JavaScript bundle to add its package dependencies.
Add these lines to any bundles:
import "trix"
import "@rails/actiontext"
Alternatively, install and setup the webpacker gem then rerun `bin/rails action_text:install`
to have these dependencies added automatically.
WARNING
end
if (application_javascript_path = destination.join("app/javascript/application.js")).exist?
insert_into_file application_javascript_path.to_s, %(import "trix"\nimport "@rails/actiontext"\n)
else
if (application_javascript_path = Rails.root.join("app/javascript/application.js")).exist?
insert_into_file application_javascript_path.to_s, %(\nimport "trix"\nimport "@rails/actiontext")
else
say <<~INSTRUCTIONS, :green
You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint.
INSTRUCTIONS
end
say <<~INSTRUCTIONS, :green
You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint.
INSTRUCTIONS
end
if (importmap_path = Rails.root.join("config/importmap.rb")).exist?
insert_into_file \
importmap_path.to_s,
%( pin "trix"\n pin "@rails/actiontext", to: "actiontext.js"\n\n),
after: "Rails.application.config.importmap.draw do\n"
else
say <<~INSTRUCTIONS, :green
You must add @rails/actiontext and trix to your importmap to reference them via ESM.
INSTRUCTIONS
end
if (importmap_path = destination.join("config/importmap.rb")).exist?
append_to_file importmap_path.to_s, %(pin "trix"\npin "@rails/actiontext", to: "actiontext.js"\n)
end
end
def create_actiontext_files
template "actiontext.css", "app/assets/stylesheets/actiontext.css"
copy_file "#{GEM_ROOT}/app/views/active_storage/blobs/_blob.html.erb",
gem_root = "#{__dir__}/../../../.."
copy_file "#{gem_root}/app/views/active_storage/blobs/_blob.html.erb",
"app/views/active_storage/blobs/_blob.html.erb"
copy_file "#{GEM_ROOT}/app/views/layouts/action_text/contents/_content.html.erb",
copy_file "#{gem_root}/app/views/layouts/action_text/contents/_content.html.erb",
"app/views/layouts/action_text/contents/_content.html.erb"
end
def enable_image_processing_gem
if (gemfile_path = Rails.root.join("Gemfile")).exist?
if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist?
say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)"
uncomment_lines gemfile_path, /gem "image_processing"/
end
@ -83,19 +55,6 @@ module ActionText
end
hook_for :test_framework
private
GEM_ROOT = "#{__dir__}/../../../.."
def js_dependencies
js_package = JSON.load(Pathname.new("#{GEM_ROOT}/package.json"))
js_package["peerDependencies"].merge \
js_package["name"] => "^#{js_package["version"]}"
end
def yarn_command(command, config = {})
in_root { run "#{Thor::Util.ruby_command} bin/yarn #{command}", abort_on_failure: true, **config }
end
end
end
end

View File

@ -133,7 +133,7 @@ module ActionView
#
# highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
# # => <a href="javascript:alert('no!')">ruby</a> on <mark>rails</mark>
def highlight(text, phrases, options = {})
def highlight(text, phrases, options = {}, &block)
text = sanitize(text) if options.fetch(:sanitize, true)
if text.blank? || phrases.blank?
@ -144,7 +144,7 @@ module ActionView
end.join("|")
if block_given?
text.gsub(/(#{match})(?![^<]*?>)/i) { |found| yield found }
text.gsub(/(#{match})(?![^<]*?>)/i, &block)
else
highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)

View File

@ -183,9 +183,9 @@ class ArelLike
def to_ary
true
end
def each
def each(&block)
a = Array.new(2) { |id| Comment.new(id + 1) }
a.each { |i| yield i }
a.each(&block)
end
end

View File

@ -28,7 +28,6 @@ namespace :test do
task "env:integration" do
ENV["AJ_INTEGRATION_TESTS"] = "1"
ENV["SKIP_REQUIRE_WEBPACKER"] = "true"
end
ACTIVEJOB_ADAPTERS.each do |adapter|

View File

@ -19,10 +19,10 @@ module ActiveJob
end
private
def tag_logger(*tags)
def tag_logger(*tags, &block)
if logger.respond_to?(:tagged)
tags.unshift "ActiveJob" unless logger_tagged_by_active_job?
logger.tagged(*tags) { yield }
logger.tagged(*tags, &block)
else
yield
end

View File

@ -49,11 +49,9 @@ class LoggingTest < ActiveSupport::TestCase
ActiveJob::Base.logger = logger
end
def subscribed
def subscribed(&block)
[].tap do |events|
ActiveSupport::Notifications.subscribed(-> (*args) { events << args }, /enqueue.*\.active_job/) do
yield
end
ActiveSupport::Notifications.subscribed(-> (*args) { events << args }, /enqueue.*\.active_job/, &block)
end
end

View File

@ -123,7 +123,7 @@ module ActiveModel
# user.serializable_hash(include: { notes: { only: 'title' }})
# # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
def serializable_hash(options = nil)
attribute_names = attributes.keys
attribute_names = self.attribute_names
return serializable_attributes(attribute_names) if options.blank?
@ -148,6 +148,11 @@ module ActiveModel
hash
end
# Returns an array of attribute names as strings
def attribute_names # :nodoc:
attributes.keys
end
private
# Hook method defining how an attribute value should be retrieved for
# serialization. By default this is assumed to be an instance named after

View File

@ -10,6 +10,50 @@
*Sam Bostock*
* Add ssl support for postgresql database tasks
Add `PGSSLMODE`, `PGSSLCERT`, `PGSSLKEY` and `PGSSLROOTCERT` to pg_env from database config
when running postgresql database tasks.
```yaml
# config/database.yml
production:
sslmode: verify-full
sslcert: client.crt
sslkey: client.key
sslrootcert: ca.crt
```
Environment variables
```
PGSSLMODE=verify-full
PGSSLCERT=client.crt
PGSSLKEY=client.key
PGSSLROOTCERT=ca.crt
```
Fixes #42994
*Michael Bayucot*
* Avoid scoping update callbacks in `ActiveRecord::Relation#update!`.
Making it consistent with how scoping is applied only to the query in `ActiveRecord::Relation#update`
and not also to the callbacks from the update itself.
*Dylan Thacker-Smith*
* Fix 2 cases that inferred polymorphic class from the association's `foreign_type`
using `String#constantize` instead of the model's `polymorphic_class_for`.
When updating a polymorphic association, the old `foreign_type` was not inferred correctly when:
1. `touch`ing the previously associated record
2. updating the previously associated record's `counter_cache`
*Jimmy Bourassa*
* Add config option for ignoring tables when dumping the schema cache.
Applications can now be configured to ignore certain tables when dumping the schema cache.
@ -30,7 +74,7 @@
* Make schema cache methods return consistent results.
Previously the schema cache methods `primary_keys`, `columns, `columns_hash`, and `indexes`
Previously the schema cache methods `primary_keys`, `columns`, `columns_hash`, and `indexes`
would behave differently than one another when a table didn't exist and differently across
database adapters. This change unifies the behavior so each method behaves the same regardless
of adapter.

View File

@ -329,6 +329,9 @@ module ActiveRecord
singleton_class.attr_accessor :verify_foreign_keys_for_fixtures
self.verify_foreign_keys_for_fixtures = false
singleton_class.attr_accessor :query_transformers
self.query_transformers = []
def self.eager_load!
super
ActiveRecord::Locking.eager_load!

View File

@ -55,7 +55,8 @@ module ActiveRecord
def decrement_counters_before_last_save
if reflection.polymorphic?
model_was = owner.attribute_before_last_save(reflection.foreign_type)&.constantize
model_type_was = owner.attribute_before_last_save(reflection.foreign_type)
model_was = owner.class.polymorphic_class_for(model_type_was) if model_type_was
else
model_was = klass
end

View File

@ -49,7 +49,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
if reflection.polymorphic?
foreign_type = reflection.foreign_type
klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
klass = klass.constantize
klass = o.class.polymorphic_class_for(klass)
else
klass = association.klass
end

View File

@ -115,9 +115,9 @@ module ActiveRecord
record[reflection.foreign_key] = nil
end
def transaction_if(value)
def transaction_if(value, &block)
if value
reflection.klass.transaction { yield }
reflection.klass.transaction(&block)
else
yield
end

View File

@ -306,14 +306,14 @@ module ActiveRecord
#
# The mysql2 and postgresql adapters support setting the transaction
# isolation level.
def transaction(requires_new: nil, isolation: nil, joinable: true)
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
if !requires_new && current_transaction.joinable?
if isolation
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
end
yield
else
transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable, &block)
end
rescue ActiveRecord::Rollback
# rollbacks are silently swallowed

View File

@ -723,7 +723,7 @@ module ActiveRecord
exception
end
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false) # :doc:
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
@instrumenter.instrument(
"sql.active_record",
sql: sql,
@ -733,14 +733,19 @@ module ActiveRecord
statement_name: statement_name,
async: async,
connection: self) do
@lock.synchronize do
yield
end
@lock.synchronize(&block)
rescue => e
raise translate_exception_class(e, sql, binds)
end
end
def transform_query(sql)
ActiveRecord.query_transformers.each do |transformer|
sql = transformer.call(sql)
end
sql
end
def translate_exception(exception, message:, sql:, binds:)
# override in derived class
case exception

View File

@ -196,14 +196,7 @@ module ActiveRecord
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil, async: false)
materialize_transactions
mark_transaction_written_if_write(sql)
log(sql, name, async: async) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@connection.query(sql)
end
end
raw_execute(sql, name, async)
end
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
@ -629,6 +622,17 @@ module ActiveRecord
emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
end
def raw_execute(sql, name, async: false)
materialize_transactions
mark_transaction_written_if_write(sql)
log(sql, name, async: async) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@connection.query(sql)
end
end
end
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
ER_DB_CREATE_EXISTS = 1007
ER_FILSORT_ABORT = 1028

View File

@ -39,13 +39,10 @@ module ActiveRecord
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil, async: false)
sql = transform_query(sql)
check_if_write_query(sql)
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
super
raw_execute(sql, name, async: async)
end
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
@ -89,9 +86,18 @@ module ActiveRecord
end
private
def raw_execute(sql, name, async: false)
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
super
end
def execute_batch(statements, name = nil)
statements = statements.map { |sql| transform_query(sql) }
combine_multi_statements(statements).each do |statement|
execute(statement, name)
raw_execute(statement, name)
end
@connection.abandon_results!
end
@ -156,6 +162,7 @@ module ActiveRecord
end
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
sql = transform_query(sql)
check_if_write_query(sql)
materialize_transactions

View File

@ -89,11 +89,9 @@ module ActiveRecord
# HELPER METHODS ===========================================
def each_hash(result) # :nodoc:
def each_hash(result, &block) # :nodoc:
if block_given?
result.each(as: :hash, symbolize_keys: true) do |row|
yield row
end
result.each(as: :hash, symbolize_keys: true, &block)
else
to_enum(:each_hash, result)
end

View File

@ -35,6 +35,7 @@ module ActiveRecord
# Note: the PG::Result object is manually memory managed; if you don't
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
def execute(sql, name = nil)
sql = transform_query(sql)
check_if_write_query(sql)
materialize_transactions

View File

@ -696,6 +696,7 @@ module ActiveRecord
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
def execute_and_clear(sql, name, binds, prepare: false, async: false)
sql = transform_query(sql)
check_if_write_query(sql)
if !prepare || without_prepared_statement?(binds)

View File

@ -19,6 +19,7 @@ module ActiveRecord
end
def execute(sql, name = nil) # :nodoc:
sql = transform_query(sql)
check_if_write_query(sql)
materialize_transactions
@ -32,6 +33,7 @@ module ActiveRecord
end
def exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
sql = transform_query(sql)
check_if_write_query(sql)
materialize_transactions
@ -113,6 +115,7 @@ module ActiveRecord
end
def execute_batch(statements, name = nil)
statements = statements.map { |sql| transform_query(sql) }
sql = combine_multi_statements(statements)
check_if_write_query(sql)

View File

@ -764,8 +764,8 @@ module ActiveRecord
model_class.name if model_class
end
def each
fixture.each { |item| yield item }
def each(&block)
fixture.each(&block)
end
def [](key)

View File

@ -51,21 +51,17 @@ module ActiveRecord
private
def read_from_primary(&blk)
ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: true) do
instrumenter.instrument("database_selector.active_record.read_from_primary") do
yield
end
instrumenter.instrument("database_selector.active_record.read_from_primary", &blk)
end
end
def read_from_replica(&blk)
ActiveRecord::Base.connected_to(role: ActiveRecord.reading_role, prevent_writes: true) do
instrumenter.instrument("database_selector.active_record.read_from_replica") do
yield
end
instrumenter.instrument("database_selector.active_record.read_from_replica", &blk)
end
end
def write_to_primary(&blk)
def write_to_primary
ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: false) do
instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
yield

View File

@ -728,16 +728,16 @@ module ActiveRecord
# end
#
# This command can be nested.
def revert(*migration_classes)
def revert(*migration_classes, &block)
run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
if block_given?
if connection.respond_to? :revert
connection.revert { yield }
connection.revert(&block)
else
recorder = command_recorder
@connection = recorder
suppress_messages do
connection.revert { yield }
connection.revert(&block)
end
@connection = recorder.delegate
recorder.replay(self)
@ -804,8 +804,8 @@ module ActiveRecord
# end
# end
# end
def up_only
execute_block { yield } unless reverting?
def up_only(&block)
execute_block(&block) unless reverting?
end
# Runs the given migration classes.
@ -1106,9 +1106,9 @@ module ActiveRecord
move(:up, steps)
end
def up(target_version = nil) # :nodoc:
def up(target_version = nil, &block) # :nodoc:
selected_migrations = if block_given?
migrations.select { |m| yield m }
migrations.select(&block)
else
migrations
end
@ -1116,9 +1116,9 @@ module ActiveRecord
Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
end
def down(target_version = nil) # :nodoc:
def down(target_version = nil, &block) # :nodoc:
selected_migrations = if block_given?
migrations.select { |m| yield m }
migrations.select(&block)
else
migrations
end
@ -1405,9 +1405,9 @@ module ActiveRecord
end
# Wrap the migration in a transaction only if supported by the adapter.
def ddl_transaction(migration)
def ddl_transaction(migration, &block)
if use_transaction?(migration)
Base.transaction { yield }
Base.transaction(&block)
else
yield
end
@ -1438,12 +1438,12 @@ module ActiveRecord
end
end
def with_advisory_lock_connection
def with_advisory_lock_connection(&block)
pool = ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
ActiveRecord::Base.connection_db_config
)
pool.with_connection { |connection| yield(connection) }
pool.with_connection(&block)
ensure
pool&.disconnect!
end

View File

@ -154,9 +154,9 @@ module ActiveRecord
include StraightReversions
def invert_transaction(args)
def invert_transaction(args, &block)
sub_recorder = CommandRecorder.new(delegate)
sub_recorder.revert { yield }
sub_recorder.revert(&block)
invertions_proc = proc {
sub_recorder.replay(self)

View File

@ -118,13 +118,14 @@ module ActiveRecord
inline_tags.pop
end
def add_query_log_tags_to_sql(sql) # :nodoc:
comments.each do |comment|
unless sql.include?(comment)
sql = prepend_comment ? "#{comment} #{sql}" : "#{sql} #{comment}"
end
def call(sql) # :nodoc:
parts = self.comments
if prepend_comment
parts << sql
else
parts.unshift(sql)
end
sql
parts.join(" ")
end
private
@ -198,15 +199,5 @@ module ActiveRecord
inline_tags.join
end
end
module ExecutionMethods
def execute(sql, *args, **kwargs)
super(ActiveRecord::QueryLogs.add_query_log_tags_to_sql(sql), *args, **kwargs)
end
def exec_query(sql, *args, **kwargs)
super(ActiveRecord::QueryLogs.add_query_log_tags_to_sql(sql), *args, **kwargs)
end
end
end
end

View File

@ -360,6 +360,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
initializer "active_record.query_log_tags_config" do |app|
config.after_initialize do
if app.config.active_record.query_log_tags_enabled
ActiveRecord.query_transformers << ActiveRecord::QueryLogs
ActiveRecord::QueryLogs.taggings.merge!(
application: Rails.application.class.name.split("::").first,
pid: -> { Process.pid },
@ -375,12 +376,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
if app.config.active_record.cache_query_log_tags
ActiveRecord::QueryLogs.cache_query_log_tags = true
end
ActiveSupport.on_load(:active_record) do
ConnectionAdapters::AbstractAdapter.descendants.each do |klass|
klass.prepend(QueryLogs::ExecutionMethods) if klass.descendants.empty?
end
end
end
end
end

View File

@ -424,14 +424,14 @@ module ActiveRecord
#
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping(all_queries: nil)
def scoping(all_queries: nil, &block)
registry = klass.scope_registry
if global_scope?(registry) && all_queries == false
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
elsif already_in_scope?(registry)
yield
else
_scoping(self, registry, all_queries) { yield }
_scoping(self, registry, all_queries, &block)
end
end
@ -498,6 +498,14 @@ module ActiveRecord
end
end
def update!(id = :all, attributes) # :nodoc:
if id == :all
each { |record| record.update!(attributes) }
else
klass.update!(id, attributes)
end
end
# Updates the counters of the records in the current relation.
#
# ==== Parameters
@ -935,11 +943,9 @@ module ActiveRecord
end
end
def skip_query_cache_if_necessary
def skip_query_cache_if_necessary(&block)
if skip_query_cache_value
uncached do
yield
end
uncached(&block)
else
yield
end

View File

@ -65,10 +65,10 @@ module ActiveRecord
#
# NOTE: By its nature, batch processing is subject to race conditions if
# other processes are modifying the database.
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block)
if block_given?
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
records.each { |record| yield record }
records.each(&block)
end
else
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do

View File

@ -47,11 +47,11 @@ module ActiveRecord
# Person.in_batches.each_record.with_index do |person, index|
# person.award_trophy(index + 1)
# end
def each_record
def each_record(&block)
return to_enum(:each_record) unless block_given?
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
relation.records.each { |record| yield record }
relation.records.each(&block)
end
end
@ -89,9 +89,9 @@ module ActiveRecord
# Person.in_batches.each do |relation|
# relation.update_all(awesome: true)
# end
def each
def each(&block)
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
return enum.each { |relation| yield relation } if block_given?
return enum.each(&block) if block_given?
enum
end
end

View File

@ -1295,11 +1295,9 @@ module ActiveRecord
nil
end
def each_join_dependencies(join_dependencies = build_join_dependencies)
def each_join_dependencies(join_dependencies = build_join_dependencies, &block)
join_dependencies.each do |join_dependency|
join_dependency.each do |join|
yield join
end
join_dependency.each(&block)
end
end

View File

@ -64,9 +64,9 @@ module ActiveRecord
# row as parameter.
#
# Returns an +Enumerator+ if no block is given.
def each
def each(&block)
if block_given?
hash_rows.each { |row| yield row }
hash_rows.each(&block)
else
hash_rows.to_enum { @rows.size }
end

View File

@ -39,8 +39,8 @@ module ActiveRecord
# Post.unscoped {
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
def unscoped
block_given? ? relation.scoping { yield } : relation
def unscoped(&block)
block_given? ? relation.scoping(&block) : relation
end
# Are there attributes associated with this scope?

View File

@ -99,10 +99,14 @@ module ActiveRecord
def psql_env
{}.tap do |env|
env["PGHOST"] = db_config.host if db_config.host
env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
env["PGHOST"] = db_config.host if db_config.host
env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
env["PGSSLMODE"] = configuration_hash[:sslmode].to_s if configuration_hash[:sslmode]
env["PGSSLCERT"] = configuration_hash[:sslcert].to_s if configuration_hash[:sslcert]
env["PGSSLKEY"] = configuration_hash[:sslkey].to_s if configuration_hash[:sslkey]
env["PGSSLROOTCERT"] = configuration_hash[:sslrootcert].to_s if configuration_hash[:sslrootcert]
end
end

View File

@ -1331,6 +1331,47 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal groucho, sponsor.thing
end
class WheelPolymorphicName < ActiveRecord::Base
self.table_name = "wheels"
belongs_to :wheelable, polymorphic: true, counter_cache: :wheels_count, touch: :wheels_owned_at
def self.polymorphic_class_for(name)
raise "Unexpected name: #{name}" unless name == "polymorphic_car"
CarPolymorphicName
end
end
class CarPolymorphicName < ActiveRecord::Base
self.table_name = "cars"
has_many :wheels, as: :wheelable
def self.polymorphic_name
"polymorphic_car"
end
end
def test_polymorphic_with_custom_name_counter_cache
car = CarPolymorphicName.create!
wheel = WheelPolymorphicName.create!(wheelable_type: "polymorphic_car", wheelable_id: car.id)
assert_equal 1, car.reload.wheels_count
wheel.update! wheelable: nil
assert_equal 0, car.reload.wheels_count
end
def test_polymorphic_with_custom_name_touch_old_belongs_to_model
car = CarPolymorphicName.create!
wheel = WheelPolymorphicName.create!(wheelable: car)
touch_time = 1.day.ago.round
travel_to(touch_time) do
wheel.update!(wheelable: nil)
end
assert_equal touch_time, car.reload.wheels_owned_at
end
def test_build_with_conditions
client = companies(:second_client)
firm = client.build_bob_firm

View File

@ -17,7 +17,7 @@ require "models/hotel"
require "models/department"
class HasManyThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase
fixtures :posts, :authors, :comments, :pirates
fixtures :posts, :authors, :comments, :pirates, :author_addresses
def setup
@author = authors(:mary)

View File

@ -2,7 +2,6 @@
require "cases/helper"
require "models/bird"
require "byebug"
class BasePreventWritesTest < ActiveRecord::TestCase
if !in_memory_db?

View File

@ -64,12 +64,10 @@ if ActiveRecord::Base.connection.supports_explain?
end
private
def stub_explain_for_query_plans(query_plans = ["query plan foo", "query plan bar"])
def stub_explain_for_query_plans(query_plans = ["query plan foo", "query plan bar"], &block)
explain_called = 0
connection.stub(:explain, proc { explain_called += 1; query_plans[explain_called - 1] }) do
yield
end
connection.stub(:explain, proc { explain_called += 1; query_plans[explain_called - 1] }, &block)
end
def bind_param(name, value)

View File

@ -755,7 +755,7 @@ unless in_memory_db?
end
private
def duel(zzz = 5)
def duel(zzz = 5, &block)
t0, t1, t2, t3 = nil, nil, nil, nil
a = Thread.new do
@ -770,7 +770,7 @@ unless in_memory_db?
b = Thread.new do
sleep zzz / 2.0 # ensure thread 1 tx starts first
t2 = Time.now
Person.transaction { yield }
Person.transaction(&block)
t3 = Time.now
end

View File

@ -1376,13 +1376,11 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
end
private
def with_bulk_change_table
def with_bulk_change_table(&block)
# Reset columns/indexes cache as we're changing the table
@columns = @indexes = nil
Person.connection.change_table(:delete_me, bulk: true) do |t|
yield t
end
Person.connection.change_table(:delete_me, bulk: true, &block)
end
def column(name)

View File

@ -759,13 +759,11 @@ class QueryCacheTest < ActiveRecord::TestCase
end
private
def with_temporary_connection_pool
def with_temporary_connection_pool(&block)
pool_config = ActiveRecord::Base.connection_handler.send(:owner_to_pool_manager).fetch("ActiveRecord::Base").get_pool_config(ActiveRecord.writing_role, :default)
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(pool_config)
pool_config.stub(:pool, new_pool) do
yield
end
pool_config.stub(:pool, new_pool, &block)
end
def middleware(&app)

View File

@ -12,16 +12,16 @@ class QueryLogsTest < ActiveRecord::TestCase
def setup
# Enable the query tags logging
ActiveRecord::Base.connection.class_eval do
prepend(ActiveRecord::QueryLogs::ExecutionMethods)
end
@original_transformers = ActiveRecord.query_transformers
@original_prepend = ActiveRecord::QueryLogs.prepend_comment
ActiveRecord.query_transformers += [ActiveRecord::QueryLogs]
ActiveRecord::QueryLogs.prepend_comment = false
ActiveRecord::QueryLogs.cache_query_log_tags = false
ActiveRecord::QueryLogs.cached_comment = nil
end
def teardown
ActiveRecord.query_transformers = @original_transformers
ActiveRecord::QueryLogs.prepend_comment = @original_prepend
ActiveRecord::QueryLogs.tags = []
end
@ -100,10 +100,10 @@ class QueryLogsTest < ActiveRecord::TestCase
ActiveRecord::QueryLogs.cache_query_log_tags = true
ActiveRecord::QueryLogs.tags = [ :application ]
assert_equal " /*application:active_record*/", ActiveRecord::QueryLogs.add_query_log_tags_to_sql("")
assert_equal " /*application:active_record*/", ActiveRecord::QueryLogs.call("")
ActiveRecord::QueryLogs.stub(:cached_comment, "/*cached_comment*/") do
assert_equal " /*cached_comment*/", ActiveRecord::QueryLogs.add_query_log_tags_to_sql("")
assert_equal " /*cached_comment*/", ActiveRecord::QueryLogs.call("")
end
ensure
ActiveRecord::QueryLogs.cached_comment = nil
@ -115,12 +115,12 @@ class QueryLogsTest < ActiveRecord::TestCase
ActiveRecord::QueryLogs.update_context(temporary: "value")
ActiveRecord::QueryLogs.tags = [ temporary_tag: ->(context) { context[:temporary] } ]
assert_equal " /*temporary_tag:value*/", ActiveRecord::QueryLogs.add_query_log_tags_to_sql("")
assert_equal " /*temporary_tag:value*/", ActiveRecord::QueryLogs.call("")
ActiveRecord::QueryLogs.update_context(temporary: "new_value")
assert_nil ActiveRecord::QueryLogs.cached_comment
assert_equal " /*temporary_tag:new_value*/", ActiveRecord::QueryLogs.add_query_log_tags_to_sql("")
assert_equal " /*temporary_tag:new_value*/", ActiveRecord::QueryLogs.call("")
ensure
ActiveRecord::QueryLogs.cached_comment = nil
ActiveRecord::QueryLogs.cache_query_log_tags = false
@ -201,15 +201,6 @@ class QueryLogsTest < ActiveRecord::TestCase
end
end
def test_inline_tags_are_deduped
ActiveRecord::QueryLogs.tags = [ :application ]
assert_sql(%r{select id from posts /\*foo\*/ /\*application:active_record\*/$}) do
ActiveRecord::QueryLogs.with_tag("foo") do
ActiveRecord::Base.connection.execute "select id from posts /*foo*/"
end
end
end
def test_empty_comments_are_not_added
original_tags = ActiveRecord::QueryLogs.tags
ActiveRecord::QueryLogs.tags = [ empty: -> { nil } ]

View File

@ -5,7 +5,7 @@ require "models/author"
module ActiveRecord
class AndTest < ActiveRecord::TestCase
fixtures :authors
fixtures :authors, :author_addresses
def test_and
david, mary, bob = authors(:david, :mary, :bob)

View File

@ -173,6 +173,24 @@ class UpdateAllTest < ActiveRecord::TestCase
end
end
def test_update_bang_on_relation
topic1 = TopicWithCallbacks.create! title: "arel", author_name: nil
topic2 = TopicWithCallbacks.create! title: "activerecord", author_name: nil
topic3 = TopicWithCallbacks.create! title: "ar", author_name: nil
topics = TopicWithCallbacks.where(id: [topic1.id, topic2.id])
topics.update!(title: "adequaterecord")
assert_equal TopicWithCallbacks.count, TopicWithCallbacks.topic_count
assert_equal "adequaterecord", topic1.reload.title
assert_equal "adequaterecord", topic2.reload.title
assert_equal "ar", topic3.reload.title
# Testing that the before_update callbacks have run
assert_equal "David", topic1.reload.author_name
assert_equal "David", topic2.reload.author_name
assert_nil topic3.reload.author_name
end
def test_update_all_cares_about_optimistic_locking
david = people(:david)

View File

@ -10,7 +10,7 @@ require "models/categorization"
module ActiveRecord
class WhereChainTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :humans, :essays
fixtures :posts, :comments, :authors, :humans, :essays, :author_addresses
def test_associated_with_association
Post.where.associated(:author).tap do |relation|

View File

@ -30,12 +30,10 @@ module ActiveRecord
$stdout, $stderr = @original_stdout, @original_stderr
end
def with_stubbed_new
def with_stubbed_new(&block)
ActiveRecord::Tasks::MySQLDatabaseTasks.stub(:new, @mysql_tasks) do
ActiveRecord::Tasks::PostgreSQLDatabaseTasks.stub(:new, @postgresql_tasks) do
ActiveRecord::Tasks::SQLiteDatabaseTasks.stub(:new, @sqlite_tasks) do
yield
end
ActiveRecord::Tasks::SQLiteDatabaseTasks.stub(:new, @sqlite_tasks, &block)
end
end
end
@ -154,13 +152,11 @@ module ActiveRecord
end
private
def with_stubbed_configurations
def with_stubbed_configurations(&block)
old_configurations = ActiveRecord::Base.configurations
ActiveRecord::Base.configurations = { "production" => { "exists" => { "database" => "my-db" } } }
assert_deprecated do
yield
end
assert_deprecated(&block)
ensure
ActiveRecord::Base.configurations = old_configurations
assert_deprecated do
@ -392,15 +388,13 @@ module ActiveRecord
end
private
def with_stubbed_configurations_establish_connection
def with_stubbed_configurations_establish_connection(&block)
old_configurations = ActiveRecord::Base.configurations
ActiveRecord::Base.configurations = @configurations
# To refrain from connecting to a newly created empty DB in
# sqlite3_mem tests
ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do
yield
end
ActiveRecord::Base.connection_handler.stub(:establish_connection, nil, &block)
ensure
ActiveRecord::Base.configurations = old_configurations
end
@ -520,13 +514,11 @@ module ActiveRecord
ActiveRecord::Base.configurations.configs_for(env_name: env_name, name: name)
end
def with_stubbed_configurations_establish_connection
def with_stubbed_configurations_establish_connection(&block)
old_configurations = ActiveRecord::Base.configurations
ActiveRecord::Base.configurations = @configurations
ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do
yield
end
ActiveRecord::Base.connection_handler.stub(:establish_connection, nil, &block)
ensure
ActiveRecord::Base.configurations = old_configurations
end
@ -637,13 +629,11 @@ module ActiveRecord
ActiveRecord::Base.configurations.configs_for(env_name: env_name, name: name)
end
def with_stubbed_configurations_establish_connection
def with_stubbed_configurations_establish_connection(&block)
old_configurations = ActiveRecord::Base.configurations
ActiveRecord::Base.configurations = @configurations
ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do
yield
end
ActiveRecord::Base.connection_handler.stub(:establish_connection, nil, &block)
ensure
ActiveRecord::Base.configurations = old_configurations
end

View File

@ -107,11 +107,9 @@ if current_adapter?(:Mysql2Adapter)
end
private
def with_stubbed_connection_establish_connection
def with_stubbed_connection_establish_connection(&block)
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
yield
end
ActiveRecord::Base.stub(:connection, @connection, &block)
end
end
end
@ -188,11 +186,9 @@ if current_adapter?(:Mysql2Adapter)
end
private
def with_stubbed_connection_establish_connection
def with_stubbed_connection_establish_connection(&block)
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
yield
end
ActiveRecord::Base.stub(:connection, @connection, &block)
end
end
end
@ -242,11 +238,9 @@ if current_adapter?(:Mysql2Adapter)
end
private
def with_stubbed_connection_establish_connection
def with_stubbed_connection_establish_connection(&block)
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
yield
end
ActiveRecord::Base.stub(:connection, @connection, &block)
end
end
end

View File

@ -138,11 +138,9 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
def with_stubbed_connection_establish_connection
def with_stubbed_connection_establish_connection(&block)
ActiveRecord::Base.stub(:connection, @connection) do
ActiveRecord::Base.stub(:establish_connection, nil) do
yield
end
ActiveRecord::Base.stub(:establish_connection, nil, &block)
end
end
end
@ -199,11 +197,9 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
def with_stubbed_connection_establish_connection
def with_stubbed_connection_establish_connection(&block)
ActiveRecord::Base.stub(:connection, @connection) do
ActiveRecord::Base.stub(:establish_connection, nil) do
yield
end
ActiveRecord::Base.stub(:establish_connection, nil, &block)
end
end
end
@ -297,10 +293,8 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
def with_stubbed_connection
ActiveRecord::Base.stub(:connection, @connection) do
yield
end
def with_stubbed_connection(&block)
ActiveRecord::Base.stub(:connection, @connection, &block)
end
end
@ -389,6 +383,18 @@ if current_adapter?(:PostgreSQLAdapter)
end
end
def test_structure_dump_with_ssl_env
expected_env = { "PGSSLMODE" => "verify-full", "PGSSLCERT" => "client.crt", "PGSSLKEY" => "client.key", "PGSSLROOTCERT" => "root.crt" }
expected_command = [expected_env, "pg_dump", "--schema-only", "--no-privileges", "--no-owner", "--file", @filename, "my-app-db"]
assert_called_with(Kernel, :system, expected_command, returns: true) do
ActiveRecord::Tasks::DatabaseTasks.structure_dump(
@configuration.merge(sslmode: "verify-full", sslcert: "client.crt", sslkey: "client.key", sslrootcert: "root.crt"),
@filename
)
end
end
def test_structure_dump_with_extra_flags
expected_command = [{}, "pg_dump", "--schema-only", "--no-privileges", "--no-owner", "--file", @filename, "--noop", "my-app-db"]
@ -556,6 +562,21 @@ if current_adapter?(:PostgreSQLAdapter)
end
end
def test_structure_load_with_ssl_env
filename = "awesome-file.sql"
expected_env = { "PGSSLMODE" => "verify-full", "PGSSLCERT" => "client.crt", "PGSSLKEY" => "client.key", "PGSSLROOTCERT" => "root.crt" }
expected_command = [expected_env, "psql", "--set", "ON_ERROR_STOP=1", "--quiet", "--no-psqlrc", "--file", filename, "--noop", @configuration["database"]]
assert_called_with(Kernel, :system, expected_command, returns: true) do
with_structure_load_flags(["--noop"]) do
ActiveRecord::Tasks::DatabaseTasks.structure_load(
@configuration.merge(sslmode: "verify-full", sslcert: "client.crt", sslkey: "client.key", sslrootcert: "root.crt"),
filename
)
end
end
end
def test_structure_load_with_hash_extra_flags_for_a_different_driver
filename = "awesome-file.sql"
expected_command = [{}, "psql", "--set", "ON_ERROR_STOP=1", "--quiet", "--no-psqlrc", "--file", filename, @configuration["database"]]

View File

@ -37,8 +37,8 @@ module ActiveRecord
SQLCounter.log.dup
end
def assert_sql(*patterns_to_match)
capture_sql { yield }
def assert_sql(*patterns_to_match, &block)
capture_sql(&block)
ensure
failed_patterns = []
patterns_to_match.each do |pattern|

View File

@ -1,3 +1,9 @@
* Emit Active Support instrumentation events from Active Storage analyzers.
Fixes #42930
*Shouichi Kamiya*
* Add support for byte range requests
*Tom Prats*

View File

@ -148,13 +148,10 @@ Active Storage, with its included JavaScript library, supports uploading directl
```html
<%= javascript_include_tag "activestorage" %>
```
Requiring via importmap (as used by Stimulus) without bundling through the asset pipeline in the application html without autostart as ESM:
```js
{
"imports": {
"@rails/activestorage": "<%= asset_path "activestorage.esm" %>"
}
}
Requiring via importmap-rails without bundling through the asset pipeline in the application html without autostart as ESM:
```ruby
# config/importmap.rb
pin "@rails/activestorage", to: "activestorage.esm.js"
```
```html
<script type="module-shim">

View File

@ -40,5 +40,9 @@ module ActiveStorage
def tmpdir # :doc:
Dir.tmpdir
end
def instrument(analyzer, &block) # :doc:
ActiveSupport::Notifications.instrument("analyze.active_storage", analyzer: analyzer, &block)
end
end
end

View File

@ -42,14 +42,16 @@ module ActiveStorage
end
def probe_from(file)
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
instrument(File.basename(ffprobe_path)) do
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
end
end
rescue Errno::ENOENT
logger.info "Skipping audio analysis because FFmpeg isn't installed"

View File

@ -12,7 +12,10 @@ module ActiveStorage
def read_image
download_blob_to_tempfile do |file|
require "mini_magick"
image = MiniMagick::Image.new(file.path)
image = instrument("mini_magick") do
MiniMagick::Image.new(file.path)
end
if image.valid?
yield image

View File

@ -12,7 +12,10 @@ module ActiveStorage
def read_image
download_blob_to_tempfile do |file|
require "ruby-vips"
image = ::Vips::Image.new_from_file(file.path, access: :sequential)
image = instrument("vips") do
::Vips::Image.new_from_file(file.path, access: :sequential)
end
if valid_image?(image)
yield image

View File

@ -121,14 +121,16 @@ module ActiveStorage
end
def probe_from(file)
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
instrument(File.basename(ffprobe_path)) do
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
end
end
rescue Errno::ENOENT
logger.info "Skipping video analysis because FFmpeg isn't installed"

View File

@ -42,6 +42,9 @@ module ActiveStorage
image/vnd.adobe.photoshop
image/vnd.microsoft.icon
image/webp
image/avif
image/heic
image/heif
)
config.active_storage.web_image_content_types = %w(

View File

@ -13,4 +13,14 @@ class ActiveStorage::Analyzer::AudioAnalyzerTest < ActiveSupport::TestCase
assert_equal 0.914286, metadata[:duration]
assert_equal 128000, metadata[:bit_rate]
end
test "instrumenting analysis" do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "audio.mp3", content_type: "audio/mp3")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "ffprobe" }, events.first.payload)
end
end

View File

@ -46,6 +46,18 @@ class ActiveStorage::Analyzer::ImageAnalyzer::ImageMagickTest < ActiveSupport::T
end
end
test "instrumenting analysis" do
analyze_with_image_magick do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "mini_magick" }, events.first.payload)
end
end
private
def analyze_with_image_magick
previous_processor, ActiveStorage.variant_processor = ActiveStorage.variant_processor, :mini_magick

View File

@ -46,6 +46,18 @@ class ActiveStorage::Analyzer::ImageAnalyzer::VipsTest < ActiveSupport::TestCase
end
end
test "instrumenting analysis" do
analyze_with_vips do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "vips" }, events.first.payload)
end
end
private
def analyze_with_vips
previous_processor, ActiveStorage.variant_processor = ActiveStorage.variant_processor, :vips

View File

@ -76,4 +76,14 @@ class ActiveStorage::Analyzer::VideoAnalyzerTest < ActiveSupport::TestCase
assert metadata[:video]
assert_not metadata[:audio]
end
test "instrumenting analysis" do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "video_without_audio_stream.mp4", content_type: "video/mp4")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "ffprobe" }, events.first.payload)
end
end

View File

@ -156,7 +156,7 @@ class ActiveStorage::AttachmentTest < ActiveSupport::TestCase
assert_equal content_type, blob.reload.content_type
end
def assert_blob_identified_outside_transaction(blob)
def assert_blob_identified_outside_transaction(blob, &block)
baseline_transaction_depth = ActiveRecord::Base.connection.open_transactions
max_transaction_depth = -1
@ -164,9 +164,7 @@ class ActiveStorage::AttachmentTest < ActiveSupport::TestCase
max_transaction_depth = [ActiveRecord::Base.connection.open_transactions, max_transaction_depth].max
end
blob.stub(:identify_without_saving, track_transaction_depth) do
yield
end
blob.stub(:identify_without_saving, track_transaction_depth, &block)
assert_equal 0, (max_transaction_depth - baseline_transaction_depth)
end

View File

@ -12,11 +12,6 @@ require "active_support/configuration_file"
require "active_storage/service/mirror_service"
require "image_processing/mini_magick"
begin
require "byebug"
rescue LoadError
end
require "active_job"
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.logger = ActiveSupport::Logger.new(nil)
@ -157,6 +152,14 @@ class ActiveSupport::TestCase
ActionController::Base.raise_on_open_redirects = old_raise_on_open_redirects
ActiveStorage::Blob.service = old_service
end
def subscribe_events_from(name)
events = []
ActiveSupport::Notifications.subscribe(name) do |*args|
events << ActiveSupport::Notifications::Event.new(*args)
end
events
end
end
require "global_id"

View File

@ -1,5 +1,16 @@
* Improves the performance of ActiveSupport::NumberHelper formatters by avoiding the use of
exceptions as flow control.
* `ActiveSupport::Dependencies` no longer installs a `const_missing` hook. Before this, you could push to the autoload paths and have constants autoloaded. This feature, known as the `classic` autoloader, has been removed.
*Xavier Noria*
* Private internal classes of `ActiveSupport::Dependencies` have been deleted, like `ActiveSupport::Dependencies::Reference`, `ActiveSupport::Dependencies::Blamable`, and others.
*Xavier Noria*
* The private API of `ActiveSupport::Dependencies` has been deleted. That includes methods like `hook!`, `unhook!`, `depend_on`, `require_or_load`, `mechanism`, and many others.
*Xavier Noria*
* Improves the performance of `ActiveSupport::NumberHelper` formatters by avoiding the use of exceptions as flow control.
*Mike Dalessio*
@ -7,8 +18,8 @@
Previously, if you provided a `error_handler` to `redis_cache_store`, any errors thrown by
the error handler would be rescued and logged only. Removed the `rescue` clause from `handle_exception`
to allow these to be thrown.
to allow these to be thrown.
*Nicholas A. Stuart*
* Allow entirely opting out of deprecation warnings.

View File

@ -36,6 +36,5 @@ Gem::Specification.new do |s|
s.add_dependency "i18n", ">= 1.6", "< 2"
s.add_dependency "tzinfo", "~> 2.0"
s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
s.add_dependency "zeitwerk", "~> 2.5.0.beta2"
s.add_dependency "minitest", ">= 5.1"
end

View File

@ -34,13 +34,13 @@ module ActiveSupport
# <% benchmark 'Process data files', level: :info, silence: true do %>
# <%= expensive_and_chatty_files_operation %>
# <% end %>
def benchmark(message = "Benchmarking", options = {})
def benchmark(message = "Benchmarking", options = {}, &block)
if logger
options.assert_valid_keys(:level, :silence)
options[:level] ||= :info
result = nil
ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
ms = Benchmark.ms { result = options[:silence] ? logger.silence(&block) : yield }
logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
result
else

View File

@ -66,8 +66,8 @@ module ActiveSupport
end
# Use a local cache for the duration of block.
def with_local_cache
use_temporary_local_cache(LocalStore.new) { yield }
def with_local_cache(&block)
use_temporary_local_cache(LocalStore.new, &block)
end
# Middleware class can be inserted as a Rack handler to be local cache for the
@ -170,8 +170,8 @@ module ActiveSupport
LocalCacheRegistry.cache_for(local_cache_key)
end
def bypass_local_cache
use_temporary_local_cache(nil) { yield }
def bypass_local_cache(&block)
use_temporary_local_cache(nil, &block)
end
def use_temporary_local_cache(temporary_cache)

View File

@ -17,14 +17,12 @@ module ActiveSupport
ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
end
def synchronize
def synchronize(&block)
Thread.handle_interrupt(EXCEPTION_NEVER) do
mon_enter
begin
Thread.handle_interrupt(EXCEPTION_IMMEDIATE) do
yield
end
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
ensure
mon_exit
end

View File

@ -215,9 +215,9 @@ module ActiveSupport
@waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } }
end
def wait_for(method)
def wait_for(method, &block)
@sleeping[Thread.current] = method
@cv.wait_while { yield }
@cv.wait_while(&block)
ensure
@sleeping.delete Thread.current
end

View File

@ -19,7 +19,7 @@ class Array
# ["1", "2"]
# ["3", "4"]
# ["5"]
def in_groups_of(number, fill_with = nil)
def in_groups_of(number, fill_with = nil, &block)
if number.to_i <= 0
raise ArgumentError,
"Group size must be a positive integer, was #{number.inspect}"
@ -36,7 +36,7 @@ class Array
end
if block_given?
collection.each_slice(number) { |slice| yield(slice) }
collection.each_slice(number, &block)
else
collection.each_slice(number).to_a
end
@ -59,7 +59,7 @@ class Array
# ["1", "2", "3"]
# ["4", "5"]
# ["6", "7"]
def in_groups(number, fill_with = nil)
def in_groups(number, fill_with = nil, &block)
# size.div number gives minor group size;
# size % number gives how many objects need extra accommodation;
# each group hold either division or division + 1 items.
@ -79,7 +79,7 @@ class Array
end
if block_given?
groups.each { |g| yield(g) }
groups.each(&block)
else
groups
end
@ -90,11 +90,11 @@ class Array
#
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
def split(value = nil)
def split(value = nil, &block)
arr = dup
result = []
if block_given?
while (idx = arr.index { |i| yield i })
while (idx = arr.index(&block))
result << arr.shift(idx)
arr.shift
end

View File

@ -11,14 +11,14 @@ module Kernel
# end
#
# noisy_call # warning voiced
def silence_warnings
with_warnings(nil) { yield }
def silence_warnings(&block)
with_warnings(nil, &block)
end
# Sets $VERBOSE to +true+ for the duration of the block and back to its
# original value afterwards.
def enable_warnings
with_warnings(true) { yield }
def enable_warnings(&block)
with_warnings(true, &block)
end
# Sets $VERBOSE for the duration of the block and back to its original

Some files were not shown because too many files have changed in this diff Show More