diff options
30 files changed, 254 insertions, 104 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 62cab7261b..7617942267 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -63,6 +63,7 @@ PATH activesupport (= 6.0.0.beta3) activestorage (6.0.0.beta3) actionpack (= 6.0.0.beta3) + activejob (= 6.0.0.beta3) activerecord (= 6.0.0.beta3) marcel (~> 0.3.1) activesupport (6.0.0.beta3) diff --git a/actionmailbox/CHANGELOG.md b/actionmailbox/CHANGELOG.md index f59c052d63..f5fb573090 100644 --- a/actionmailbox/CHANGELOG.md +++ b/actionmailbox/CHANGELOG.md @@ -11,6 +11,7 @@ *Pratik Naik* + ## Rails 6.0.0.beta1 (January 18, 2019) ## * Added to Rails. diff --git a/actionpack/test/controller/new_base/render_file_test.rb b/actionpack/test/controller/new_base/render_file_test.rb index de8af029e0..82325c5bb2 100644 --- a/actionpack/test/controller/new_base/render_file_test.rb +++ b/actionpack/test/controller/new_base/render_file_test.rb @@ -40,32 +40,44 @@ module RenderFile testing RenderFile::BasicController test "rendering simple template" do - get :index + assert_deprecated do + get :index + end assert_response "Hello world!" end test "rendering template with ivar" do - get :with_instance_variables + assert_deprecated do + get :with_instance_variables + end assert_response "The secret is in the sauce\n" end test "rendering a relative path" do - get :relative_path + assert_deprecated do + get :relative_path + end assert_response "The secret is in the sauce\n" end test "rendering a relative path with dot" do - get :relative_path_with_dot + assert_deprecated do + get :relative_path_with_dot + end assert_response "The secret is in the sauce\n" end test "rendering a Pathname" do - get :pathname + assert_deprecated do + get :pathname + end assert_response "The secret is in the sauce\n" end test "rendering file with locals" do - get :with_locals + assert_deprecated do + get :with_locals + end assert_response "The secret is in the sauce\n" end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 4750093c5c..6d198ca42f 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -325,7 +325,7 @@ class ExpiresInRenderTest < ActionController::TestCase def test_dynamic_render_with_file # This is extremely bad, but should be possible to do. assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__)) - response = get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' } + response = assert_deprecated { get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' } } assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)), response.body end @@ -351,7 +351,7 @@ class ExpiresInRenderTest < ActionController::TestCase def test_permitted_dynamic_render_file_hash assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__)) - response = get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } } + response = assert_deprecated { get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } } } assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)), response.body end diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb index ae8330e029..3d5161f207 100644 --- a/actionpack/test/controller/renderer_test.rb +++ b/actionpack/test/controller/renderer_test.rb @@ -40,7 +40,7 @@ class RendererTest < ActiveSupport::TestCase test "rendering with an instance renderer" do renderer = ApplicationController.renderer.new - content = renderer.render file: "test/hello_world" + content = assert_deprecated { renderer.render file: "test/hello_world" } assert_equal "Hello world!", content end diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 43688fc8a7..17a5782f9f 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -14,18 +14,19 @@ ## Rails 6.0.0.beta2 (February 25, 2019) ## -* ActionView::Template.finalize_compiled_template_methods is deprecated with +* `ActionView::Template.finalize_compiled_template_methods` is deprecated with no replacement. *tenderlove* -* config.action_view.finalize_compiled_template_methods is deprecated with +* `config.action_view.finalize_compiled_template_methods` is deprecated with no replacement. *tenderlove* * Ensure unique DOM IDs for collection inputs with float values. - Fixes #34974 + + Fixes #34974. *Mark Edmondson* diff --git a/actionview/lib/action_view/file_template.rb b/actionview/lib/action_view/file_template.rb index dea02176eb..e0dc7da3b6 100644 --- a/actionview/lib/action_view/file_template.rb +++ b/actionview/lib/action_view/file_template.rb @@ -11,7 +11,7 @@ module ActionView end def source - File.binread @filename + ::File.binread @filename end def refresh(_) diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index 9548fe12c4..60cb2ceafa 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -26,7 +26,12 @@ module ActionView elsif options.key?(:html) Template::HTML.new(options[:html], formats.first) elsif options.key?(:file) - @lookup_context.with_fallbacks.find_file(options[:file], nil, false, keys, @details) + if File.exist?(options[:file]) + Template::File.new(options[:file]) + else + ActiveSupport::Deprecation.warn "render file: should be given the absolute path to a file" + @lookup_context.with_fallbacks.find_file(options[:file], nil, false, keys, @details) + end elsif options.key?(:inline) handler = Template.handler_for_extension(options[:type] || "erb") format = if handler.respond_to?(:default_format) diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 7bc5e86edb..ebe52532d6 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -113,6 +113,7 @@ module ActionView eager_autoload do autoload :Error + autoload :File autoload :Handlers autoload :HTML autoload :Inline @@ -139,7 +140,7 @@ module ActionView @virtual_path = virtual_path @variable = if @virtual_path - base = @virtual_path[-1] == "/" ? "" : File.basename(@virtual_path) + base = @virtual_path[-1] == "/" ? "" : ::File.basename(@virtual_path) base =~ /\A_?(.*?)(?:\.\w+)*\z/ $1.to_sym end diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb index d0ea03e228..aace0be04d 100644 --- a/actionview/lib/action_view/template/error.rb +++ b/actionview/lib/action_view/template/error.rb @@ -104,7 +104,7 @@ module ActionView def line_number @line_number ||= if file_name - regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ + regexp = /#{Regexp.escape ::File.basename(file_name)}:(\d+)/ $1 if message =~ regexp || backtrace.find { |line| line =~ regexp } end end diff --git a/actionview/lib/action_view/template/file.rb b/actionview/lib/action_view/template/file.rb new file mode 100644 index 0000000000..487e5735cf --- /dev/null +++ b/actionview/lib/action_view/template/file.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module ActionView #:nodoc: + # = Action View File Template + class Template #:nodoc: + class File #:nodoc: + attr_accessor :type, :format + + def initialize(filename) + @filename = filename.to_s + extname = ::File.extname(filename).delete(".") + @type = Template::Types[extname] || Template::Types[:text] + @format = @type.symbol + end + + def identifier + @filename + end + + def render(*args) + ::File.read(@filename) + end + + def formats; Array(format); end + deprecate :formats + end + end +end diff --git a/actionview/test/actionpack/abstract/render_test.rb b/actionview/test/actionpack/abstract/render_test.rb index d863548a5c..e4e8ac93b2 100644 --- a/actionview/test/actionpack/abstract/render_test.rb +++ b/actionview/test/actionpack/abstract/render_test.rb @@ -26,7 +26,7 @@ module AbstractController end def file - render file: "some/file" + ActiveSupport::Deprecation.silence { render file: "some/file" } end def inline diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index 52c3c54d96..c8ce7366d1 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -872,48 +872,64 @@ class RenderTest < ActionController::TestCase # :ported: def test_render_file_with_instance_variables - get :render_file_with_instance_variables + assert_deprecated do + get :render_file_with_instance_variables + end assert_equal "The secret is in the sauce\n", @response.body end def test_render_file - get :hello_world_file + assert_deprecated do + get :hello_world_file + end assert_equal "Hello world!", @response.body end # :ported: def test_render_file_not_using_full_path - get :render_file_not_using_full_path + assert_deprecated do + get :render_file_not_using_full_path + end assert_equal "The secret is in the sauce\n", @response.body end # :ported: def test_render_file_not_using_full_path_with_dot_in_path - get :render_file_not_using_full_path_with_dot_in_path + assert_deprecated do + get :render_file_not_using_full_path_with_dot_in_path + end assert_equal "The secret is in the sauce\n", @response.body end # :ported: def test_render_file_using_pathname - get :render_file_using_pathname + assert_deprecated do + get :render_file_using_pathname + end assert_equal "The secret is in the sauce\n", @response.body end # :ported: def test_render_file_with_locals - get :render_file_with_locals + assert_deprecated do + get :render_file_with_locals + end assert_equal "The secret is in the sauce\n", @response.body end # :ported: def test_render_file_as_string_with_locals - get :render_file_as_string_with_locals + assert_deprecated do + get :render_file_as_string_with_locals + end assert_equal "The secret is in the sauce\n", @response.body end # :assessed: def test_render_file_from_template - get :render_file_from_template + assert_deprecated do + get :render_file_from_template + end assert_equal "The secret is in the sauce\n", @response.body end @@ -1133,11 +1149,19 @@ class RenderTest < ActionController::TestCase end def test_bad_render_to_string_still_throws_exception - assert_raise(ActionView::MissingTemplate) { get :render_to_string_with_exception } + assert_deprecated do + assert_raise(ActionView::MissingTemplate) do + get :render_to_string_with_exception + end + end end def test_render_to_string_that_throws_caught_exception_doesnt_break_assigns - assert_nothing_raised { get :render_to_string_with_caught_exception } + assert_deprecated do + assert_nothing_raised do + get :render_to_string_with_caught_exception + end + end assert_equal "i'm before the render", @controller.instance_variable_get(:@before) assert_equal "i'm after the render", @controller.instance_variable_get(:@after) end diff --git a/actionview/test/template/log_subscriber_test.rb b/actionview/test/template/log_subscriber_test.rb index 85735139c1..8b160a7336 100644 --- a/actionview/test/template/log_subscriber_test.rb +++ b/actionview/test/template/log_subscriber_test.rb @@ -51,9 +51,20 @@ class AVLogSubscriberTest < ActiveSupport::TestCase def @view.combined_fragment_cache_key(*); "ahoy `controller` dependency"; end end + def test_render_template_template + Rails.stub(:root, File.expand_path(FIXTURE_LOAD_PATH)) do + @view.render(template: "test/hello_world") + wait + + assert_equal 2, @logger.logged(:info).size + assert_match(/Rendering test\/hello_world\.erb/, @logger.logged(:info).first) + assert_match(/Rendered test\/hello_world\.erb/, @logger.logged(:info).last) + end + end + def test_render_file_template Rails.stub(:root, File.expand_path(FIXTURE_LOAD_PATH)) do - @view.render(file: "test/hello_world") + @view.render(file: "#{FIXTURE_LOAD_PATH}/test/hello_world.erb") wait assert_equal 2, @logger.logged(:info).size diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index baff7cd83f..15c41051de 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -53,15 +53,20 @@ module RenderTestCases assert_match(/You invoked render but did not give any of (.+) option\./, e.message) end + def test_render_template + assert_equal "Hello world!", @view.render(template: "test/hello_world") + end + + def test_render_file - assert_equal "Hello world!", @view.render(file: "test/hello_world") + assert_equal "Hello world!", assert_deprecated { @view.render(file: "test/hello_world") } end # Test if :formats, :locale etc. options are passed correctly to the resolvers. def test_render_file_with_format - assert_match "<h1>No Comment</h1>", @view.render(file: "comments/empty", formats: [:html]) - assert_match "<error>No Comment</error>", @view.render(file: "comments/empty", formats: [:xml]) - assert_match "<error>No Comment</error>", @view.render(file: "comments/empty", formats: :xml) + assert_match "<h1>No Comment</h1>", assert_deprecated { @view.render(file: "comments/empty", formats: [:html]) } + assert_match "<error>No Comment</error>", assert_deprecated { @view.render(file: "comments/empty", formats: [:xml]) } + assert_match "<error>No Comment</error>", assert_deprecated { @view.render(file: "comments/empty", formats: :xml) } end def test_render_template_with_format @@ -94,8 +99,8 @@ module RenderTestCases end def test_render_file_with_locale - assert_equal "<h1>Kein Kommentar</h1>", @view.render(file: "comments/empty", locale: [:de]) - assert_equal "<h1>Kein Kommentar</h1>", @view.render(file: "comments/empty", locale: :de) + assert_equal "<h1>Kein Kommentar</h1>", assert_deprecated { @view.render(file: "comments/empty", locale: [:de]) } + assert_equal "<h1>Kein Kommentar</h1>", assert_deprecated { @view.render(file: "comments/empty", locale: :de) } end def test_render_template_with_locale @@ -107,8 +112,8 @@ module RenderTestCases end def test_render_file_with_handlers - assert_equal "<h1>No Comment</h1>\n", @view.render(file: "comments/empty", handlers: [:builder]) - assert_equal "<h1>No Comment</h1>\n", @view.render(file: "comments/empty", handlers: :builder) + assert_equal "<h1>No Comment</h1>\n", assert_deprecated { @view.render(file: "comments/empty", handlers: [:builder]) } + assert_equal "<h1>No Comment</h1>\n", assert_deprecated { @view.render(file: "comments/empty", handlers: :builder) } end def test_render_template_with_handlers @@ -156,22 +161,27 @@ module RenderTestCases assert_equal "Elastica", @view.render(template: "/shared") end - def test_render_file_with_full_path + def test_render_file_with_full_path_no_extension template_path = File.expand_path("../fixtures/test/hello_world", __dir__) + assert_equal "Hello world!", assert_deprecated { @view.render(file: template_path) } + end + + def test_render_file_with_full_path + template_path = File.expand_path("../fixtures/test/hello_world.erb", __dir__) assert_equal "Hello world!", @view.render(file: template_path) end def test_render_file_with_instance_variables - assert_equal "The secret is in the sauce\n", @view.render(file: "test/render_file_with_ivar") + assert_equal "The secret is in the sauce\n", assert_deprecated { @view.render(file: "test/render_file_with_ivar") } end def test_render_file_with_locals locals = { secret: "in the sauce" } - assert_equal "The secret is in the sauce\n", @view.render(file: "test/render_file_with_locals", locals: locals) + assert_equal "The secret is in the sauce\n", assert_deprecated { @view.render(file: "test/render_file_with_locals", locals: locals) } end def test_render_file_not_using_full_path_with_dot_in_path - assert_equal "The secret is in the sauce\n", @view.render(file: "test/dot.directory/render_file_with_ivar") + assert_equal "The secret is in the sauce\n", assert_deprecated { @view.render(file: "test/dot.directory/render_file_with_ivar") } end def test_render_partial_from_default @@ -292,7 +302,7 @@ module RenderTestCases end def test_render_file_with_errors - e = assert_raises(ActionView::Template::Error) { @view.render(file: File.expand_path("test/_raise", FIXTURE_LOAD_PATH)) } + e = assert_raises(ActionView::Template::Error) { assert_deprecated { @view.render(file: File.expand_path("test/_raise", FIXTURE_LOAD_PATH)) } } assert_match %r!method.*doesnt_exist!, e.message assert_equal "", e.sub_template_message assert_equal "1", e.line_number diff --git a/actionview/test/template/streaming_render_test.rb b/actionview/test/template/streaming_render_test.rb index a5b59a700e..a5e673e71e 100644 --- a/actionview/test/template/streaming_render_test.rb +++ b/actionview/test/template/streaming_render_test.rb @@ -47,12 +47,12 @@ class FiberedTest < SetupFiberedBase end def test_render_file - assert_equal "Hello world!", buffered_render(file: "test/hello_world") + assert_equal "Hello world!", assert_deprecated { buffered_render(file: "test/hello_world") } end def test_render_file_with_locals locals = { secret: "in the sauce" } - assert_equal "The secret is in the sauce\n", buffered_render(file: "test/render_file_with_locals", locals: locals) + assert_equal "The secret is in the sauce\n", assert_deprecated { buffered_render(file: "test/render_file_with_locals", locals: locals) } end def test_render_partial diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 74a0913b6f..ad87abfa3a 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -41,12 +41,12 @@ Before: Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"}) - => #<Day id: nil, day: "0001-01-03", created_at: nil, updated_at: nil> + # => #<Day id: nil, day: "0001-01-03", created_at: nil, updated_at: nil> After: Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"}) - => #<Day id: nil, day: "0001-01-01", created_at: nil, updated_at: nil> + # => #<Day id: nil, day: "0001-01-01", created_at: nil, updated_at: nil> Fixes #28521. diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 21861ced31..432ac641c6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,10 +1,10 @@ * Assign all attributes before calling `build` to ensure the child record is visible in `before_add` and `after_add` callbacks for `has_many :through` associations. - + Fixes #33249. - + *Ryan H. Kerr* - + * Add `ActiveRecord::Relation#extract_associated` for extracting associated records from a relation. ``` diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index b6049bdab4..0d384950fe 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -60,9 +60,8 @@ module ActiveRecord attributes = through_scope_attributes attributes[source_reflection.name] = record attributes[source_reflection.foreign_type] = options[:source_type] if options[:source_type] - through_record = through_association.build(attributes) - through_record + through_association.build(attributes) end end diff --git a/activestorage/activestorage.gemspec b/activestorage/activestorage.gemspec index 34029ac8ad..0ae2dcdd3e 100644 --- a/activestorage/activestorage.gemspec +++ b/activestorage/activestorage.gemspec @@ -28,7 +28,8 @@ Gem::Specification.new do |s| # NOTE: Please read our dependency guidelines before updating versions: # https://edgeguides.rubyonrails.org/security.html#dependency-management-and-cves - s.add_dependency "actionpack", version + s.add_dependency "actionpack", version + s.add_dependency "activejob", version s.add_dependency "activerecord", version s.add_dependency "marcel", "~> 0.3.1" diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index 384e6ebfa6..fc75a8f816 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true require "rails" +require "action_controller/railtie" +require "active_job/railtie" +require "active_record/railtie" + require "active_storage" require "active_storage/previewer/poppler_pdf_previewer" diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index f30249cb3f..6eb0af69bf 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,16 +1,25 @@ +* In `:zeitwerk` mode, eager load directories in engines and applications only + if present in their respective `config.eager_load_paths`. + + A common use case for this is adding `lib` to `config.autoload_paths`, but + not to `config.eager_load_paths`. In that configuration, for example, files + in the `lib` directory should not be eager loaded. + + *Xavier Noria* + * Fix bug in Range comparisons when comparing to an excluded-end Range Before: - (1..10).cover?(1...11) => false + (1..10).cover?(1...11) # => false After: - (1..10).cover?(1...11) => true + (1..10).cover?(1...11) # => true With the same change for `Range#include?` and `Range#===`. - *Owen Stephens* + *Owen Stephens* * Use weak references in descendants tracker to allow anonymous subclasses to be garbage collected. @@ -27,11 +36,11 @@ * Fix `Time#advance` to work with dates before 1001-03-07 Before: - + Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-05 00:00:00 UTC - + After - + Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-06 00:00:00 UTC Note that this doesn't affect `DateTime#advance` as that doesn't use a proleptic calendar. @@ -46,27 +55,27 @@ I18n.backend.store_translations(:de, i18n: { transliterate: { rule: { "ü" => "ue" } } }) - ActiveSupport::Inflector.transliterate("ü", locale: :de) => "ue" - "Fünf autos".parameterize(locale: :de) => "fuenf-autos" - ActiveSupport::Inflector.parameterize("Fünf autos", locale: :de) => "fuenf-autos" + ActiveSupport::Inflector.transliterate("ü", locale: :de) # => "ue" + "Fünf autos".parameterize(locale: :de) # => "fuenf-autos" + ActiveSupport::Inflector.parameterize("Fünf autos", locale: :de) # => "fuenf-autos" *Kaan Ozkan*, *Sharang Dashputre* * Allow Array#excluding and Enumerable#excluding to deal with a passed array gracefully. - [ 1, 2, 3, 4, 5 ].excluding([4, 5]) => [ 1, 2, 3 ] + [ 1, 2, 3, 4, 5 ].excluding([4, 5]) # => [ 1, 2, 3 ] *DHH* -* Renamed Array#without and Enumerable#without to Array#excluding and Enumerable#excluding, to create parity with - Array#including and Enumerable#including. Retained the old names as aliases. +* Renamed `Array#without` and `Enumerable#without` to `Array#excluding` and `Enumerable#excluding`, to create parity with + `Array#including` and `Enumerable#including`. Retained the old names as aliases. *DHH* -* Added Array#including and Enumerable#including to conveniently enlarge a collection with more members using a method rather than an operator: +* Added `Array#including` and `Enumerable#including` to conveniently enlarge a collection with more members using a method rather than an operator: - [ 1, 2, 3 ].including(4, 5) => [ 1, 2, 3, 4, 5 ] - post.authors.including(Current.person) => All the authors plus the current person! + [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ] + post.authors.including(Current.person) # => All the authors plus the current person! *DHH* diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb index 10e4c6b09d..ea01e5891c 100644 --- a/activesupport/lib/active_support/core_ext/array/access.rb +++ b/activesupport/lib/active_support/core_ext/array/access.rb @@ -31,16 +31,16 @@ class Array # Returns a new array that includes the passed elements. # - # [ 1, 2, 3 ].including(4, 5) => [ 1, 2, 3, 4, 5 ] - # [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) => [ [ 0, 1 ], [ 1, 0 ] ] + # [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ] + # [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ] def including(*elements) self + elements.flatten(1) end # Returns a copy of the Array excluding the specified elements. # - # ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") => ["David", "Rafael"] - # [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) => [ [ 0, 1 ] ] + # ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"] + # [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ] # # Note: This is an optimization of <tt>Enumerable#excluding</tt> that uses <tt>Array#-</tt> # instead of <tt>Array#reject</tt> for performance reasons. diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index d5d00b5e6e..82f07c085e 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -70,6 +70,11 @@ module ActiveSupport #:nodoc: # only once. All directories in this set must also be present in +autoload_paths+. mattr_accessor :autoload_once_paths, default: [] + # This is a private set that collects all eager load paths during bootstrap. + # Useful for Zeitwerk integration. Its public interface is the config.* path + # accessors of each engine. + mattr_accessor :_eager_load_paths, default: Set.new + # An array of qualified constant names that have been loaded. Adding a name # to this array will cause it to be unloaded the next time Dependencies are # cleared. diff --git a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb index c6fdade006..a43d03cf09 100644 --- a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb +++ b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "set" require "active_support/core_ext/string/inflections" module ActiveSupport @@ -52,7 +53,7 @@ module ActiveSupport class << self def take_over setup_autoloaders - freeze_autoload_paths + freeze_paths decorate_dependencies end @@ -64,11 +65,11 @@ module ActiveSupport # prevent misconfigurations. next unless File.directory?(autoload_path) - if autoload_once?(autoload_path) - Rails.autoloaders.once.push_dir(autoload_path) - else - Rails.autoloaders.main.push_dir(autoload_path) - end + autoloader = \ + autoload_once?(autoload_path) ? Rails.autoloaders.once : Rails.autoloaders.main + + autoloader.push_dir(autoload_path) + autoloader.do_not_eager_load(autoload_path) unless eager_load?(autoload_path) end Rails.autoloaders.each(&:setup) @@ -78,9 +79,14 @@ module ActiveSupport Dependencies.autoload_once_paths.include?(autoload_path) end - def freeze_autoload_paths + def eager_load?(autoload_path) + Dependencies._eager_load_paths.member?(autoload_path) + end + + def freeze_paths Dependencies.autoload_paths.freeze Dependencies.autoload_once_paths.freeze + Dependencies._eager_load_paths.freeze end def decorate_dependencies diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index 50d4a4c57d..270e4a3bf9 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -465,7 +465,6 @@ number of digits after the decimal point. * `default` Allows to set a default value on the column. Note that if you are using a dynamic value (such as a date), the default will only be calculated the first time (i.e. on the date the migration is applied). -* `index` Adds an index for the column. * `comment` Adds a comment for the column. Some adapters may support additional options; see the adapter specific API docs diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index cc6e08aaec..e40f16e62d 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -65,6 +65,7 @@ The methods are: * `distinct` * `eager_load` * `extending` +* `extract_associated` * `from` * `group` * `having` diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index df0d6d4fa0..549a9b6de0 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -6,7 +6,7 @@ * Add `config.disable_sandbox` option to Rails console. This setting will disable `rails console --sandbox` mode, preventing - developer from accidentally starting a sandbox console, + developer from accidentally starting a sandbox console, which when left inactive, can cause the database server to run out of memory. *Prem Sichanugrist* @@ -15,6 +15,7 @@ *Yuji Yaginuma* + ## Rails 6.0.0.beta3 (March 11, 2019) ## * Generate random development secrets @@ -29,7 +30,6 @@ *Eileen M. Uchitelle*, *Aaron Patterson*, *John Hawthorn* - ## Rails 6.0.0.beta2 (February 25, 2019) ## * Fix non-symbol access to nested hashes returned from `Rails::Application.config_for` diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 07bd56c978..9b3698aa5e 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -469,13 +469,16 @@ module Rails self end - # Eager load the application by loading all ruby - # files inside eager_load paths. def eager_load! - if Rails.autoloaders.zeitwerk_enabled? - eager_load_with_zeitwerk! - else - eager_load_with_dependencies! + # Already done by Zeitwerk::Loader.eager_load_all in the finisher. + return if Rails.autoloaders.zeitwerk_enabled? + + config.eager_load_paths.each do |load_path| + # Starts after load_path plus a slash, ends before ".rb". + relname_range = (load_path.to_s.length + 1)...-3 + Dir.glob("#{load_path}/**/*.rb").sort.each do |file| + require_dependency file[relname_range] + end end end @@ -567,12 +570,15 @@ module Rails ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths) ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths) - # Freeze so future modifications will fail rather than do nothing mysteriously config.autoload_paths.freeze - config.eager_load_paths.freeze config.autoload_once_paths.freeze end + initializer :set_eager_load_paths, before: :bootstrap_hook do + ActiveSupport::Dependencies._eager_load_paths.merge(config.eager_load_paths) + config.eager_load_paths.freeze + end + initializer :add_routing_paths do |app| routing_paths = paths["config/routes.rb"].existent @@ -651,22 +657,6 @@ module Rails private - def eager_load_with_zeitwerk! - (config.eager_load_paths - Zeitwerk::Loader.all_dirs).each do |path| - Dir.glob("#{path}/**/*.rb").sort.each { |file| require file } - end - end - - def eager_load_with_dependencies! - config.eager_load_paths.each do |load_path| - # Starts after load_path plus a slash, ends before ".rb". - relname_range = (load_path.to_s.length + 1)...-3 - Dir.glob("#{load_path}/**/*.rb").sort.each do |file| - require_dependency file[relname_range] - end - end - end - def load_config_initializer(initializer) # :doc: ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do load(initializer) diff --git a/railties/test/application/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb index c82b37d07d..5d2e34433a 100644 --- a/railties/test/application/zeitwerk_integration_test.rb +++ b/railties/test/application/zeitwerk_integration_test.rb @@ -124,12 +124,26 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true" app_file "app/models/post.rb", "class Post; end; $zeitwerk_integration_test_post = true" + boot("production") assert $zeitwerk_integration_test_user assert $zeitwerk_integration_test_post end + test "eager loading loads code in engines" do + $test_blog_engine_eager_loaded = false + + engine("blog") do |bukkit| + bukkit.write("lib/blog.rb", "class BlogEngine < Rails::Engine; end") + bukkit.write("app/models/post.rb", "Post = $test_blog_engine_eager_loaded = true") + end + + boot("production") + + assert $test_blog_engine_eager_loaded + end + test "eager loading loads anything managed by Zeitwerk" do $zeitwerk_integration_test_user = false app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true" @@ -149,6 +163,34 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase assert $zeitwerk_integration_test_extras end + test "autoload directories not present in eager load paths are not eager loaded" do + $zeitwerk_integration_test_user = false + app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true" + + $zeitwerk_integration_test_lib = false + app_dir "lib" + app_file "lib/webhook_hacks.rb", "WebhookHacks = 1; $zeitwerk_integration_test_lib = true" + + $zeitwerk_integration_test_extras = false + app_dir "extras" + app_file "extras/websocket_hacks.rb", "WebsocketHacks = 1; $zeitwerk_integration_test_extras = true" + + add_to_config "config.autoload_paths << '#{app_path}/lib'" + add_to_config "config.autoload_once_paths << '#{app_path}/extras'" + + boot("production") + + assert $zeitwerk_integration_test_user + assert !$zeitwerk_integration_test_lib + assert !$zeitwerk_integration_test_extras + + assert WebhookHacks + assert WebsocketHacks + + assert $zeitwerk_integration_test_lib + assert $zeitwerk_integration_test_extras + end + test "autoload_paths are set as root dirs of main, and in the same order" do boot |