aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock2
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb3
-rw-r--r--activerecord/lib/active_record/core.rb3
-rw-r--r--activerecord/lib/active_record/scoping/default.rb4
-rw-r--r--activestorage/lib/active_storage/attached/many.rb6
-rw-r--r--activestorage/lib/active_storage/attached/one.rb8
-rw-r--r--activestorage/test/models/attachments_test.rb46
-rw-r--r--activesupport/lib/active_support/cache/redis_cache_store.rb6
-rw-r--r--activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb17
-rw-r--r--activesupport/test/concurrency/load_interlock_aware_monitor_test.rb55
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/lib/rails/application.rb16
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb4
-rw-r--r--railties/test/generators/plugin_generator_test.rb32
15 files changed, 186 insertions, 24 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index 8c678fa660..f02dfa8f60 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -371,7 +371,7 @@ GEM
rack (>= 0.4)
rack-protection (2.0.0)
rack
- rack-test (0.7.0)
+ rack-test (0.7.1)
rack (>= 1.0, < 3)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index cf51c47068..2c43a8c29f 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -283,8 +283,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
with_test_route_set(expire_after: 5.hours) do
# First request accesses the session
time = Time.local(2008, 4, 24)
- cookie_body = nil
-
Time.stub :now, time do
expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
@@ -303,6 +301,8 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
Time.stub :now, time do
expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
+ cookies[SessionKey] = SignedBar
+
get "/no_session_access"
assert_response :success
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 345983a655..7e6db860dd 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -5,6 +5,7 @@ require "active_record/connection_adapters/schema_cache"
require "active_record/connection_adapters/sql_type_metadata"
require "active_record/connection_adapters/abstract/schema_dumper"
require "active_record/connection_adapters/abstract/schema_creation"
+require "active_support/concurrency/load_interlock_aware_monitor"
require "arel/collectors/bind"
require "arel/collectors/composite"
require "arel/collectors/sql_string"
@@ -107,7 +108,7 @@ module ActiveRecord
@schema_cache = SchemaCache.new self
@quoted_column_names, @quoted_table_names = {}, {}
@visitor = arel_visitor
- @lock = Monitor.new
+ @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index b97b14644e..481159e501 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -277,7 +277,8 @@ module ActiveRecord
relation = Relation.create(self, arel_table, predicate_builder)
if finder_needs_type_condition? && !ignore_default_scope?
- relation.where(type_condition).create_with(inheritance_column.to_s => sti_name)
+ relation.where!(type_condition)
+ relation.create_with!(inheritance_column.to_s => sti_name)
else
relation
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 86ae374318..8c612df27a 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -111,7 +111,7 @@ module ActiveRecord
# The user has defined their own default scope method, so call that
evaluate_default_scope do
if scope = default_scope
- (base_rel ||= relation).merge(scope)
+ (base_rel ||= relation).merge!(scope)
end
end
elsif default_scopes.any?
@@ -119,7 +119,7 @@ module ActiveRecord
evaluate_default_scope do
default_scopes.inject(base_rel) do |default_scope, scope|
scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
- default_scope.merge(base_rel.instance_exec(&scope))
+ default_scope.merge!(base_rel.instance_exec(&scope))
end
end
end
diff --git a/activestorage/lib/active_storage/attached/many.rb b/activestorage/lib/active_storage/attached/many.rb
index 0b3400bccf..6eace65b79 100644
--- a/activestorage/lib/active_storage/attached/many.rb
+++ b/activestorage/lib/active_storage/attached/many.rb
@@ -20,7 +20,11 @@ module ActiveStorage
# document.images.attach([ first_blob, second_blob ])
def attach(*attachables)
attachables.flatten.collect do |attachable|
- attachments.create!(name: name, blob: create_blob_from(attachable))
+ if record.new_record?
+ attachments.build(record: record, blob: create_blob_from(attachable))
+ else
+ attachments.create!(record: record, blob: create_blob_from(attachable))
+ end
end
end
diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb
index 7092f6b109..0244232b2c 100644
--- a/activestorage/lib/active_storage/attached/one.rb
+++ b/activestorage/lib/active_storage/attached/one.rb
@@ -23,7 +23,7 @@ module ActiveStorage
if attached? && dependent == :purge_later
replace attachable
else
- write_attachment create_attachment_from(attachable)
+ write_attachment build_attachment_from(attachable)
end
end
@@ -67,13 +67,13 @@ module ActiveStorage
blob.tap do
transaction do
detach
- write_attachment create_attachment_from(attachable)
+ write_attachment build_attachment_from(attachable)
end
end.purge_later
end
- def create_attachment_from(attachable)
- ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable))
+ def build_attachment_from(attachable)
+ ActiveStorage::Attachment.new(record: record, name: name, blob: create_blob_from(attachable))
end
def write_attachment(attachment)
diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb
index e645d868ce..edd68d3051 100644
--- a/activestorage/test/models/attachments_test.rb
+++ b/activestorage/test/models/attachments_test.rb
@@ -27,7 +27,7 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
test "attach new blob from an UploadedFile" do
file = file_fixture "racecar.jpg"
- @user.avatar.attach Rack::Test::UploadedFile.new file
+ @user.avatar.attach Rack::Test::UploadedFile.new file.to_s
assert_equal "racecar.jpg", @user.avatar.filename.to_s
end
@@ -56,6 +56,26 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
assert ActiveStorage::Blob.service.exist?(@user.avatar.key)
end
+ test "attach blob to new record" do
+ user = User.new(name: "Jason")
+
+ assert_no_changes -> { user.new_record? } do
+ assert_no_difference -> { ActiveStorage::Attachment.count } do
+ user.avatar.attach create_blob(filename: "funky.jpg")
+ end
+ end
+
+ assert user.avatar.attached?
+ assert_equal "funky.jpg", user.avatar.filename.to_s
+
+ assert_difference -> { ActiveStorage::Attachment.count }, +1 do
+ user.save!
+ end
+
+ assert user.reload.avatar.attached?
+ assert_equal "funky.jpg", user.avatar.filename.to_s
+ end
+
test "access underlying associations of new blob" do
@user.avatar.attach create_blob(filename: "funky.jpg")
assert_equal @user, @user.avatar_attachment.record
@@ -160,6 +180,30 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
assert_equal "country.jpg", @user.highlights.second.filename.to_s
end
+ test "attach blobs to new record" do
+ user = User.new(name: "Jason")
+
+ assert_no_changes -> { user.new_record? } do
+ assert_no_difference -> { ActiveStorage::Attachment.count } do
+ user.highlights.attach(
+ { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
+ { io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
+ end
+ end
+
+ assert user.highlights.attached?
+ assert_equal "town.jpg", user.highlights.first.filename.to_s
+ assert_equal "country.jpg", user.highlights.second.filename.to_s
+
+ assert_difference -> { ActiveStorage::Attachment.count }, +2 do
+ user.save!
+ end
+
+ assert user.reload.highlights.attached?
+ assert_equal "town.jpg", user.highlights.first.filename.to_s
+ assert_equal "country.jpg", user.highlights.second.filename.to_s
+ end
+
test "find attached blobs" do
@user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb
index 358de9203d..08200a556f 100644
--- a/activesupport/lib/active_support/cache/redis_cache_store.rb
+++ b/activesupport/lib/active_support/cache/redis_cache_store.rb
@@ -300,10 +300,10 @@ module ActiveSupport
options = names.extract_options!
options = merged_options(options)
- keys_to_names = names.map { |name| [ normalize_key(name, options), name ] }.to_h
- values = redis.mget(*keys_to_names.keys)
+ keys = names.map { |name| normalize_key(name, options) }
+ values = redis.mget(*keys)
- keys_to_names.zip(values).each_with_object({}) do |((key, name), value), results|
+ names.zip(values).each_with_object({}) do |(name, value), results|
if value
entry = deserialize_entry(value)
unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
diff --git a/activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb b/activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb
new file mode 100644
index 0000000000..a8455c0048
--- /dev/null
+++ b/activesupport/lib/active_support/concurrency/load_interlock_aware_monitor.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "monitor"
+
+module ActiveSupport
+ module Concurrency
+ # A monitor that will permit dependency loading while blocked waiting for
+ # the lock.
+ class LoadInterlockAwareMonitor < Monitor
+ # Enters an exclusive section, but allows dependency loading while blocked
+ def mon_enter
+ mon_try_enter ||
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
+ end
+ end
+ end
+end
diff --git a/activesupport/test/concurrency/load_interlock_aware_monitor_test.rb b/activesupport/test/concurrency/load_interlock_aware_monitor_test.rb
new file mode 100644
index 0000000000..2d0f45ec5f
--- /dev/null
+++ b/activesupport/test/concurrency/load_interlock_aware_monitor_test.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "concurrent/atomic/count_down_latch"
+require "active_support/concurrency/load_interlock_aware_monitor"
+
+module ActiveSupport
+ module Concurrency
+ class LoadInterlockAwareMonitorTest < ActiveSupport::TestCase
+ def setup
+ @monitor = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
+ end
+
+ def test_entering_with_no_blocking
+ assert @monitor.mon_enter
+ end
+
+ def test_entering_with_blocking
+ load_interlock_latch = Concurrent::CountDownLatch.new
+ monitor_latch = Concurrent::CountDownLatch.new
+
+ able_to_use_monitor = false
+ able_to_load = false
+
+ thread_with_load_interlock = Thread.new do
+ ActiveSupport::Dependencies.interlock.running do
+ load_interlock_latch.count_down
+ monitor_latch.wait
+
+ @monitor.synchronize do
+ able_to_use_monitor = true
+ end
+ end
+ end
+
+ thread_with_monitor_lock = Thread.new do
+ @monitor.synchronize do
+ monitor_latch.count_down
+ load_interlock_latch.wait
+
+ ActiveSupport::Dependencies.interlock.loading do
+ able_to_load = true
+ end
+ end
+ end
+
+ thread_with_load_interlock.join
+ thread_with_monitor_lock.join
+
+ assert able_to_use_monitor
+ assert able_to_load
+ end
+ end
+ end
+end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index e69a1231b1..d086248278 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Deprecate `after_bundle` callback in Rails plugin templates.
+
+ *Yuji Yaginuma*
+
* `rails new` and `rails plugin new` get `Active Storage` by default.
Add ability to skip `Active Storage` with `--skip-active-storage`
and do so automatically when `--skip-active-record` is used.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index df266fbfce..b1429df18b 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -429,23 +429,23 @@ module Rails
end
end
- # Decrypts the credentials hash as kept in `config/credentials.yml.enc`. This file is encrypted with
- # the Rails master key, which is either taken from ENV["RAILS_MASTER_KEY"] or from loading
- # `config/master.key`.
+ # Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with
+ # the Rails master key, which is either taken from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading
+ # +config/master.key+.
def credentials
@credentials ||= encrypted("config/credentials.yml.enc")
end
# Shorthand to decrypt any encrypted configurations or files.
#
- # For any file added with `bin/rails encrypted:edit` call `read` to decrypt
+ # For any file added with <tt>bin/rails encrypted:edit</tt> call +read+ to decrypt
# the file with the master key.
- # The master key is either stored in `config/master.key` or `ENV["RAILS_MASTER_KEY"]`.
+ # The master key is either stored in +config/master.key+ or <tt>ENV["RAILS_MASTER_KEY"]</tt>.
#
# Rails.application.encrypted("config/mystery_man.txt.enc").read
# # => "We've met before, haven't we?"
#
- # It's also possible to interpret encrypted YAML files with `config`.
+ # It's also possible to interpret encrypted YAML files with +config+.
#
# Rails.application.encrypted("config/credentials.yml.enc").config
# # => { next_guys_line: "I don't think so. Where was it you think we met?" }
@@ -456,11 +456,11 @@ module Rails
# # => "I don't think so. Where was it you think we met?"
#
# The files or configs can also be encrypted with a custom key. To decrypt with
- # a key in the `ENV`, use:
+ # a key in the +ENV+, use:
#
# Rails.application.encrypted("config/special_tokens.yml.enc", env_key: "SPECIAL_TOKENS")
#
- # Or to decrypt with a file, that should be version control ignored, relative to `Rails.root`:
+ # Or to decrypt with a file, that should be version control ignored, relative to +Rails.root+:
#
# Rails.application.encrypted("config/special_tokens.yml.enc", key_path: "config/special_tokens.key")
def encrypted(path, key_path: "config/master.key", env_key: "RAILS_MASTER_KEY")
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 786aea503c..a83c911806 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -263,6 +263,10 @@ task default: :test
public_task :apply_rails_template
def run_after_bundle_callbacks
+ unless @after_bundle_callbacks.empty?
+ ActiveSupport::Deprecation.warn("`after_bundle` is deprecated and will be removed in the next version of Rails. ")
+ end
+
@after_bundle_callbacks.each do |callback|
callback.call
end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 06f59ee33d..fc7584c175 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -721,6 +721,38 @@ class PluginGeneratorTest < Rails::Generators::TestCase
Object.send(:remove_const, "ENGINE_ROOT")
end
+ def test_after_bundle_callback
+ path = "http://example.org/rails_template"
+ template = %{ after_bundle { run "echo ran after_bundle" } }.dup
+ template.instance_eval "def read; self; end" # Make the string respond to read
+
+ check_open = -> *args do
+ assert_equal [ path, "Accept" => "application/x-thor-template" ], args
+ template
+ end
+
+ sequence = ["echo ran after_bundle"]
+ @sequence_step ||= 0
+ ensure_bundler_first = -> command do
+ assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}"
+ @sequence_step += 1
+ end
+
+ content = nil
+ generator([destination_root], template: path).stub(:open, check_open, template) do
+ generator.stub(:bundle_command, ensure_bundler_first) do
+ generator.stub(:run, ensure_bundler_first) do
+ silence_stream($stdout) do
+ content = capture(:stderr) { generator.invoke_all }
+ end
+ end
+ end
+ end
+
+ assert_equal 1, @sequence_step
+ assert_match(/DEPRECATION WARNING: `after_bundle` is deprecated/, content)
+ end
+
private
def action(*args, &block)