Merge branch 'main' into use-precision-in-updated-at-upsert
This commit is contained in:
commit
6324abe5c3
|
@ -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/
|
||||
|
|
|
@ -143,6 +143,9 @@ Style/DefWithParentheses:
|
|||
Style/MethodDefParentheses:
|
||||
Enabled: true
|
||||
|
||||
Style/ExplicitBlockArgument:
|
||||
Enabled: true
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: true
|
||||
EnforcedStyle: always
|
||||
|
|
6
Gemfile
6
Gemfile
|
@ -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"
|
||||
|
|
39
Gemfile.lock
39
Gemfile.lock
|
@ -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!
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
require "cases/helper"
|
||||
require "models/bird"
|
||||
require "byebug"
|
||||
|
||||
class BasePreventWritesTest < ActiveRecord::TestCase
|
||||
if !in_memory_db?
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 } ]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]]
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue