railties 6.0.0.beta3 → 6.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -7
  3. data/RDOC_MAIN.rdoc +3 -3
  4. data/README.rdoc +1 -1
  5. data/lib/rails/application.rb +3 -1
  6. data/lib/rails/application/configuration.rb +27 -1
  7. data/lib/rails/application/default_middleware_stack.rb +1 -0
  8. data/lib/rails/application/dummy_erb_compiler.rb +19 -0
  9. data/lib/rails/application/finisher.rb +38 -1
  10. data/lib/rails/autoloaders.rb +10 -2
  11. data/lib/rails/command/environment_argument.rb +7 -4
  12. data/lib/rails/commands/console/console_command.rb +6 -0
  13. data/lib/rails/commands/credentials/USAGE +1 -1
  14. data/lib/rails/commands/credentials/credentials_command.rb +17 -3
  15. data/lib/rails/commands/dbconsole/dbconsole_command.rb +19 -7
  16. data/lib/rails/commands/dev/dev_command.rb +4 -2
  17. data/lib/rails/commands/encrypted/USAGE +28 -0
  18. data/lib/rails/commands/encrypted/encrypted_command.rb +1 -0
  19. data/lib/rails/commands/initializers/initializers_command.rb +7 -0
  20. data/lib/rails/commands/notes/notes_command.rb +1 -1
  21. data/lib/rails/commands/runner/runner_command.rb +7 -3
  22. data/lib/rails/commands/server/server_command.rb +8 -6
  23. data/lib/rails/engine.rb +27 -31
  24. data/lib/rails/gem_version.rb +1 -1
  25. data/lib/rails/generators.rb +2 -0
  26. data/lib/rails/generators/app_base.rb +2 -2
  27. data/lib/rails/generators/app_name.rb +2 -2
  28. data/lib/rails/generators/database.rb +1 -1
  29. data/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +6 -3
  30. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +8 -0
  31. data/lib/rails/generators/generated_attribute.rb +36 -10
  32. data/lib/rails/generators/named_base.rb +1 -0
  33. data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -3
  34. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +8 -0
  35. data/lib/rails/generators/rails/app/templates/bin/setup.tt +3 -2
  36. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +2 -0
  37. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +2 -0
  38. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +10 -5
  39. data/lib/rails/generators/rails/app/templates/ruby-version.tt +1 -1
  40. data/lib/rails/generators/rails/assets/assets_generator.rb +7 -0
  41. data/lib/rails/generators/rails/db/system/change/change_generator.rb +12 -2
  42. data/lib/rails/generators/rails/plugin/plugin_generator.rb +0 -15
  43. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +8 -0
  44. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +1 -1
  45. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +1 -1
  46. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +2 -2
  47. data/lib/rails/mailers_controller.rb +6 -3
  48. data/lib/rails/source_annotation_extractor.rb +14 -1
  49. data/lib/rails/tasks.rb +1 -0
  50. data/lib/rails/tasks/zeitwerk.rake +78 -0
  51. metadata +14 -12
  52. data/lib/rails/generators/rails/app/templates/bin/update.tt +0 -33
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/time"
4
+ require "active_support/deprecation"
4
5
 
5
6
  module Rails
6
7
  module Generators
@@ -51,6 +52,12 @@ module Rails
51
52
  type = $1
52
53
  provided_options = $2.split(/[,.-]/)
53
54
  options = Hash[provided_options.map { |opt| [opt.to_sym, true] }]
55
+
56
+ if options[:required]
57
+ ActiveSupport::Deprecation.warn("Passing {required} option has no effect on the model generator. It will be removed in Rails 6.1.\n")
58
+ options.delete(:required)
59
+ end
60
+
54
61
  return type, options
55
62
  else
56
63
  return type, {}
@@ -68,13 +75,15 @@ module Rails
68
75
 
69
76
  def field_type
70
77
  @field_type ||= case type
71
- when :integer then :number_field
72
- when :float, :decimal then :text_field
73
- when :time then :time_select
74
- when :datetime, :timestamp then :datetime_select
75
- when :date then :date_select
76
- when :text then :text_area
77
- when :boolean then :check_box
78
+ when :integer then :number_field
79
+ when :float, :decimal then :text_field
80
+ when :time then :time_select
81
+ when :datetime, :timestamp then :datetime_select
82
+ when :date then :date_select
83
+ when :text then :text_area
84
+ when :rich_text then :rich_text_area
85
+ when :boolean then :check_box
86
+ when :attachment, :attachments then :file_field
78
87
  else
79
88
  :text_field
80
89
  end
@@ -90,7 +99,9 @@ module Rails
90
99
  when :string then name == "type" ? "" : "MyString"
91
100
  when :text then "MyText"
92
101
  when :boolean then false
93
- when :references, :belongs_to then nil
102
+ when :references, :belongs_to,
103
+ :attachment, :attachments,
104
+ :rich_text then nil
94
105
  else
95
106
  ""
96
107
  end
@@ -133,7 +144,7 @@ module Rails
133
144
  end
134
145
 
135
146
  def required?
136
- attr_options[:required]
147
+ reference? && Rails.application.config.active_record.belongs_to_required_by_default
137
148
  end
138
149
 
139
150
  def has_index?
@@ -152,6 +163,22 @@ module Rails
152
163
  type == :token
153
164
  end
154
165
 
166
+ def rich_text?
167
+ type == :rich_text
168
+ end
169
+
170
+ def attachment?
171
+ type == :attachment
172
+ end
173
+
174
+ def attachments?
175
+ type == :attachments
176
+ end
177
+
178
+ def virtual?
179
+ rich_text? || attachment? || attachments?
180
+ end
181
+
155
182
  def inject_options
156
183
  (+"").tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
157
184
  end
@@ -163,7 +190,6 @@ module Rails
163
190
  def options_for_migration
164
191
  @attr_options.dup.tap do |options|
165
192
  if required?
166
- options.delete(:required)
167
193
  options[:null] = false
168
194
  end
169
195
 
@@ -187,6 +187,7 @@ module Rails
187
187
 
188
188
  def attributes_names # :doc:
189
189
  @attributes_names ||= attributes.each_with_object([]) do |a, names|
190
+ next if a.attachments?
190
191
  names << a.column_name
191
192
  names << "password_confirmation" if a.password_digest?
192
193
  names << "#{a.name}_type" if a.polymorphic?
@@ -28,7 +28,7 @@ ruby <%= "'#{RUBY_VERSION}'" -%>
28
28
 
29
29
  <% if depend_on_bootsnap? -%>
30
30
  # Reduces boot times through caching; required in config/boot.rb
31
- gem 'bootsnap', '>= 1.4.1', require: false
31
+ gem 'bootsnap', '>= 1.4.2', require: false
32
32
 
33
33
  <%- end -%>
34
34
  <%- if options.api? -%>
@@ -69,8 +69,8 @@ group :test do
69
69
  # Adds support for Capybara system testing and selenium driver
70
70
  gem 'capybara', '>= 2.15'
71
71
  gem 'selenium-webdriver'
72
- # Easy installation and use of chromedriver to run system tests with Chrome
73
- gem 'chromedriver-helper'
72
+ # Easy installation and use of web drivers to run system tests with browsers
73
+ gem 'webdrivers'
74
74
  end
75
75
  <%- end -%>
76
76
 
@@ -13,3 +13,11 @@ require("@rails/activestorage").start()
13
13
  <%- unless options[:skip_action_cable] -%>
14
14
  require("channels")
15
15
  <%- end -%>
16
+
17
+
18
+ // Uncomment to copy all static images under ../images to the output folder and reference
19
+ // them with the image_pack_tag helper in views (e.g <%%= image_pack_tag 'rails.png' %>)
20
+ // or the `imagePath` JavaScript helper below.
21
+ //
22
+ // const images = require.context('../images', true)
23
+ // const imagePath = (name) => images(name, true)
@@ -8,7 +8,8 @@ def system!(*args)
8
8
  end
9
9
 
10
10
  FileUtils.chdir APP_ROOT do
11
- # This script is a starting point to setup your application.
11
+ # This script is a way to setup or update your development environment automatically.
12
+ # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
12
13
  # Add necessary setup steps to this file.
13
14
 
14
15
  puts '== Installing dependencies =='
@@ -27,7 +28,7 @@ FileUtils.chdir APP_ROOT do
27
28
  # end
28
29
 
29
30
  puts "\n== Preparing database =="
30
- system! 'bin/rails db:setup'
31
+ system! 'bin/rails db:prepare'
31
32
  <% end -%>
32
33
 
33
34
  puts "\n== Removing old logs and tempfiles =="
@@ -15,9 +15,11 @@ Rails.application.configure do
15
15
  # Enable/disable caching. By default caching is disabled.
16
16
  # Run rails dev:cache to toggle caching.
17
17
  if Rails.root.join('tmp', 'caching-dev.txt').exist?
18
+ <%- unless options.api? -%>
18
19
  config.action_controller.perform_caching = true
19
20
  config.action_controller.enable_fragment_cache_logging = true
20
21
 
22
+ <%- end -%>
21
23
  config.cache_store = :memory_store
22
24
  config.public_file_server.headers = {
23
25
  'Cache-Control' => "public, max-age=#{2.days.to_i}"
@@ -12,7 +12,9 @@ Rails.application.configure do
12
12
 
13
13
  # Full error reports are disabled and caching is turned on.
14
14
  config.consider_all_requests_local = false
15
+ <%- unless options.api? -%>
15
16
  config.action_controller.perform_caching = true
17
+ <%- end -%>
16
18
 
17
19
  # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18
20
  # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
@@ -1,11 +1,16 @@
1
+ # The test environment is used exclusively to run your application's
2
+ # test suite. You never need to work with it otherwise. Remember that
3
+ # your test database is "scratch space" for the test suite and is wiped
4
+ # and recreated between test runs. Don't rely on the data there!
5
+
1
6
  Rails.application.configure do
2
7
  # Settings specified here will take precedence over those in config/application.rb.
3
-
4
- # The test environment is used exclusively to run your application's
5
- # test suite. You never need to work with it otherwise. Remember that
6
- # your test database is "scratch space" for the test suite and is wiped
7
- # and recreated between test runs. Don't rely on the data there!
8
+ <%# Spring executes the reloaders when files change. %>
9
+ <%- if spring_install? -%>
10
+ config.cache_classes = false
11
+ <%- else -%>
8
12
  config.cache_classes = true
13
+ <%- end -%>
9
14
 
10
15
  # Do not eager load code on boot. This avoids loading your whole application
11
16
  # just for the purpose of running a single test. If you are using a tool that
@@ -1 +1 @@
1
- <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" -%>
1
+ <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
@@ -3,7 +3,10 @@
3
3
  module Rails
4
4
  module Generators
5
5
  class AssetsGenerator < NamedBase # :nodoc:
6
+ class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
6
7
  class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
8
+
9
+ class_option :javascript_engine, desc: "Engine for JavaScripts"
7
10
  class_option :stylesheet_engine, desc: "Engine for Stylesheets"
8
11
 
9
12
  private
@@ -11,6 +14,10 @@ module Rails
11
14
  file_name
12
15
  end
13
16
 
17
+ hook_for :javascript_engine do |javascript_engine|
18
+ invoke javascript_engine, [name] if options[:javascripts]
19
+ end
20
+
14
21
  hook_for :stylesheet_engine do |stylesheet_engine|
15
22
  invoke stylesheet_engine, [name] if options[:stylesheets]
16
23
  end
@@ -35,8 +35,9 @@ module Rails
35
35
  end
36
36
 
37
37
  def edit_gemfile
38
- database_gem_name, _ = gem_for_database
39
- gsub_file("Gemfile", all_database_gems_regex, database_gem_name)
38
+ name, version = gem_for_database
39
+ gsub_file("Gemfile", all_database_gems_regex, name)
40
+ gsub_file("Gemfile", gem_entry_regex_for(name), gem_entry_for(name, *version))
40
41
  end
41
42
 
42
43
  private
@@ -48,6 +49,15 @@ module Rails
48
49
  all_database_gem_names = all_database_gems.map(&:first)
49
50
  /(\b#{all_database_gem_names.join('\b|\b')}\b)/
50
51
  end
52
+
53
+ def gem_entry_regex_for(gem_name)
54
+ /^gem.*\b#{gem_name}\b.*/
55
+ end
56
+
57
+ def gem_entry_for(*gem_name_and_version)
58
+ gem_name_and_version.map! { |segment| "'#{segment}'" }
59
+ "gem #{gem_name_and_version.join(", ")}"
60
+ end
51
61
  end
52
62
  end
53
63
  end
@@ -144,17 +144,6 @@ task default: :test
144
144
  end
145
145
  end
146
146
 
147
- def javascripts
148
- return if options.skip_javascript?
149
-
150
- if mountable?
151
- template "rails/javascripts.js",
152
- "app/assets/javascripts/#{namespaced_name}/application.js"
153
- elsif full?
154
- empty_directory_with_keep_file "app/assets/javascripts/#{namespaced_name}"
155
- end
156
- end
157
-
158
147
  def bin(force = false)
159
148
  bin_file = engine? ? "bin/rails.tt" : "bin/test.tt"
160
149
  template bin_file, force: force do |content|
@@ -236,10 +225,6 @@ task default: :test
236
225
  build(:stylesheets) unless api?
237
226
  end
238
227
 
239
- def create_javascript_files
240
- build(:javascripts) unless api?
241
- end
242
-
243
228
  def create_bin_files
244
229
  build(:bin)
245
230
  end
@@ -32,6 +32,14 @@ module Rails
32
32
  hook_for :helper, as: :scaffold do |invoked|
33
33
  invoke invoked, [ controller_name ]
34
34
  end
35
+
36
+ private
37
+
38
+ def permitted_params
39
+ params = attributes_names.map { |name| ":#{name}" }.join(", ")
40
+ params += attributes.select(&:attachments?).map { |a| ", #{a.name}: []" }.join
41
+ params
42
+ end
35
43
  end
36
44
  end
37
45
  end
@@ -54,7 +54,7 @@ class <%= controller_class_name %>Controller < ApplicationController
54
54
  <%- if attributes_names.empty? -%>
55
55
  params.fetch(:<%= singular_table_name %>, {})
56
56
  <%- else -%>
57
- params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
57
+ params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
58
58
  <%- end -%>
59
59
  end
60
60
  end
@@ -61,7 +61,7 @@ class <%= controller_class_name %>Controller < ApplicationController
61
61
  <%- if attributes_names.empty? -%>
62
62
  params.fetch(:<%= singular_table_name %>, {})
63
63
  <%- else -%>
64
- params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
64
+ params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
65
65
  <%- end -%>
66
66
  end
67
67
  end
@@ -1,4 +1,4 @@
1
- # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
1
+ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
2
  <% unless attributes.empty? -%>
3
3
  <% %w(one two).each do |name| %>
4
4
  <%= name %>:
@@ -7,7 +7,7 @@
7
7
  password_digest: <%%= BCrypt::Password.create('secret') %>
8
8
  <%- elsif attribute.reference? -%>
9
9
  <%= yaml_key_value(attribute.column_name.sub(/_id$/, ''), attribute.default || name) %>
10
- <%- else -%>
10
+ <%- elsif !attribute.virtual? -%>
11
11
  <%= yaml_key_value(attribute.column_name, attribute.default) %>
12
12
  <%- end -%>
13
13
  <%- if attribute.polymorphic? -%>
@@ -5,8 +5,9 @@ require "rails/application_controller"
5
5
  class Rails::MailersController < Rails::ApplicationController # :nodoc:
6
6
  prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
7
7
 
8
+ around_action :set_locale, only: :preview
9
+ before_action :find_preview, only: :preview
8
10
  before_action :require_local!, unless: :show_previews?
9
- before_action :find_preview, :set_locale, only: :preview
10
11
 
11
12
  helper_method :part_query, :locale_query
12
13
 
@@ -38,7 +39,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
38
39
  end
39
40
  else
40
41
  @part = find_preferred_part(request.format, Mime[:html], Mime[:text])
41
- render action: "email", layout: false, formats: %w[html]
42
+ render action: "email", layout: false, formats: [:html]
42
43
  end
43
44
  else
44
45
  raise AbstractController::ActionNotFound, "Email '#{@email_action}' not found in #{@preview.name}"
@@ -92,6 +93,8 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
92
93
  end
93
94
 
94
95
  def set_locale
95
- I18n.locale = params[:locale] || I18n.default_locale
96
+ I18n.with_locale(params[:locale] || I18n.default_locale) do
97
+ yield
98
+ end
96
99
  end
97
100
  end
@@ -29,6 +29,16 @@ module Rails
29
29
  directories.push(*dirs)
30
30
  end
31
31
 
32
+ def self.tags
33
+ @@tags ||= %w(OPTIMIZE FIXME TODO)
34
+ end
35
+
36
+ # Registers additional tags
37
+ # Rails::SourceAnnotationExtractor::Annotation.register_tags("TESTME", "DEPRECATEME")
38
+ def self.register_tags(*additional_tags)
39
+ tags.push(*additional_tags)
40
+ end
41
+
32
42
  def self.extensions
33
43
  @@extensions ||= {}
34
44
  end
@@ -66,6 +76,8 @@ module Rails
66
76
  # Prints all annotations with tag +tag+ under the root directories +app+,
67
77
  # +config+, +db+, +lib+, and +test+ (recursively).
68
78
  #
79
+ # If +tag+ is <tt>nil</tt>, annotations with either default or registered tags are printed.
80
+ #
69
81
  # Specific directories can be explicitly set using the <tt>:dirs</tt> key in +options+.
70
82
  #
71
83
  # Rails::SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
@@ -75,7 +87,8 @@ module Rails
75
87
  # See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
76
88
  #
77
89
  # This class method is the single entry point for the `rails notes` command.
78
- def self.enumerate(tag, options = {})
90
+ def self.enumerate(tag = nil, options = {})
91
+ tag ||= Annotation.tags.join("|")
79
92
  extractor = new(tag)
80
93
  dirs = options.delete(:dirs) || Annotation.directories
81
94
  extractor.display(extractor.find(dirs), options)
@@ -15,6 +15,7 @@ require "rake"
15
15
  routes
16
16
  tmp
17
17
  yarn
18
+ zeitwerk
18
19
  ).tap { |arr|
19
20
  arr << "statistics" if Rake.application.current_scope.empty?
20
21
  }.each do |task|
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ ensure_classic_mode = ->() do
4
+ if Rails.autoloaders.zeitwerk_enabled?
5
+ abort <<~EOS
6
+ Please, enable temporarily :classic mode:
7
+
8
+ # config/application.rb
9
+ config.autoloader = :classic
10
+
11
+ and try again. When all is good, you can delete that line.
12
+ EOS
13
+ end
14
+ end
15
+
16
+ eager_load = ->() do
17
+ Rails.configuration.eager_load_namespaces.each(&:eager_load!)
18
+ end
19
+
20
+ mismatches = []
21
+ check_directory = ->(directory, parent) do
22
+ # test/mailers/previews might not exist.
23
+ return unless File.exists?(directory)
24
+
25
+ Dir.foreach(directory) do |entry|
26
+ next if entry.start_with?(".")
27
+ next if parent == Object && entry == "concerns"
28
+
29
+ abspath = File.join(directory, entry)
30
+
31
+ if File.directory?(abspath) || abspath.end_with?(".rb")
32
+ print "."
33
+ cname = File.basename(abspath, ".rb").camelize.to_sym
34
+ if parent.const_defined?(cname, false)
35
+ if File.directory?(abspath)
36
+ check_directory[abspath, parent.const_get(cname)]
37
+ end
38
+ else
39
+ mismatches << [abspath, parent, cname]
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ report = ->() do
46
+ puts
47
+ if mismatches.empty?
48
+ puts "All is good!"
49
+ puts "Please, remember to delete `config.autoloader = :classic` from config/application.rb."
50
+ else
51
+ mismatches.each do |abspath, parent, cname|
52
+ relpath = abspath.sub(%r{\A#{Regexp.escape(Rails.root.to_path)}/}, "")
53
+ cpath = parent == Object ? cname : "#{parent.name}::#{cname}"
54
+ puts "expected #{relpath} to define #{cpath}"
55
+ end
56
+ puts
57
+ puts <<~EOS
58
+ Please revise the reported mismatches. You can normally fix them by adding
59
+ acronyms to config/initializers/inflections.rb or renaming the constants.
60
+ EOS
61
+ end
62
+ end
63
+
64
+ namespace :zeitwerk do
65
+ desc "Checks project structure for Zeitwerk compatibility"
66
+ task check: :environment do
67
+ ensure_classic_mode[]
68
+ eager_load[]
69
+
70
+ $stdout.sync = true
71
+ ActiveSupport::Dependencies.autoload_paths.each do |autoload_path|
72
+ check_directory[autoload_path, Object]
73
+ end
74
+ puts
75
+
76
+ report[]
77
+ end
78
+ end