railties 7.0.10 → 7.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +565 -234
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.md +99 -0
  5. data/README.rdoc +4 -4
  6. data/lib/minitest/rails_plugin.rb +63 -0
  7. data/lib/rails/api/task.rb +35 -4
  8. data/lib/rails/app_updater.rb +1 -1
  9. data/lib/rails/application/bootstrap.rb +12 -3
  10. data/lib/rails/application/configuration.rb +179 -67
  11. data/lib/rails/application/default_middleware_stack.rb +8 -2
  12. data/lib/rails/application/dummy_config.rb +19 -0
  13. data/lib/rails/application/finisher.rb +40 -33
  14. data/lib/rails/application.rb +116 -31
  15. data/lib/rails/backtrace_cleaner.rb +1 -1
  16. data/lib/rails/cli.rb +5 -2
  17. data/lib/rails/command/actions.rb +10 -12
  18. data/lib/rails/command/base.rb +55 -53
  19. data/lib/rails/command/environment_argument.rb +32 -16
  20. data/lib/rails/command/helpers/editor.rb +17 -12
  21. data/lib/rails/command.rb +84 -33
  22. data/lib/rails/commands/about/about_command.rb +14 -0
  23. data/lib/rails/commands/application/application_command.rb +2 -0
  24. data/lib/rails/commands/console/console_command.rb +14 -14
  25. data/lib/rails/commands/credentials/USAGE +53 -55
  26. data/lib/rails/commands/credentials/credentials_command/diffing.rb +5 -3
  27. data/lib/rails/commands/credentials/credentials_command.rb +64 -70
  28. data/lib/rails/commands/db/system/change/change_command.rb +2 -1
  29. data/lib/rails/commands/dbconsole/dbconsole_command.rb +25 -115
  30. data/lib/rails/commands/destroy/destroy_command.rb +3 -2
  31. data/lib/rails/commands/dev/dev_command.rb +1 -6
  32. data/lib/rails/commands/encrypted/USAGE +15 -20
  33. data/lib/rails/commands/encrypted/encrypted_command.rb +46 -35
  34. data/lib/rails/commands/gem_help/USAGE +16 -0
  35. data/lib/rails/commands/gem_help/gem_help_command.rb +13 -0
  36. data/lib/rails/commands/generate/generate_command.rb +2 -2
  37. data/lib/rails/commands/help/USAGE +13 -13
  38. data/lib/rails/commands/help/help_command.rb +21 -2
  39. data/lib/rails/commands/initializers/initializers_command.rb +1 -4
  40. data/lib/rails/commands/middleware/middleware_command.rb +17 -0
  41. data/lib/rails/commands/new/new_command.rb +2 -0
  42. data/lib/rails/commands/notes/notes_command.rb +2 -1
  43. data/lib/rails/commands/plugin/plugin_command.rb +2 -0
  44. data/lib/rails/commands/rake/rake_command.rb +25 -22
  45. data/lib/rails/commands/restart/restart_command.rb +14 -0
  46. data/lib/rails/commands/routes/routes_command.rb +13 -1
  47. data/lib/rails/commands/runner/USAGE +14 -12
  48. data/lib/rails/commands/runner/runner_command.rb +32 -20
  49. data/lib/rails/commands/secret/secret_command.rb +13 -0
  50. data/lib/rails/commands/secrets/USAGE +44 -49
  51. data/lib/rails/commands/secrets/secrets_command.rb +19 -38
  52. data/lib/rails/commands/server/server_command.rb +32 -31
  53. data/lib/rails/commands/test/USAGE +14 -0
  54. data/lib/rails/commands/test/test_command.rb +56 -14
  55. data/lib/rails/commands/unused_routes/unused_routes_command.rb +75 -0
  56. data/lib/rails/commands/version/version_command.rb +1 -0
  57. data/lib/rails/configuration.rb +5 -5
  58. data/lib/rails/console/app.rb +1 -4
  59. data/lib/rails/deprecator.rb +7 -0
  60. data/lib/rails/engine/configuration.rb +5 -0
  61. data/lib/rails/engine.rb +36 -16
  62. data/lib/rails/gem_version.rb +4 -4
  63. data/lib/rails/generators/actions.rb +6 -15
  64. data/lib/rails/generators/active_model.rb +14 -28
  65. data/lib/rails/generators/app_base.rb +353 -82
  66. data/lib/rails/generators/app_name.rb +3 -14
  67. data/lib/rails/generators/base.rb +17 -9
  68. data/lib/rails/generators/database.rb +19 -1
  69. data/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt +1 -1
  70. data/lib/rails/generators/generated_attribute.rb +2 -1
  71. data/lib/rails/generators/migration.rb +1 -2
  72. data/lib/rails/generators/model_helpers.rb +2 -1
  73. data/lib/rails/generators/rails/app/USAGE +15 -6
  74. data/lib/rails/generators/rails/app/app_generator.rb +84 -60
  75. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +107 -0
  76. data/lib/rails/generators/rails/app/templates/Gemfile.tt +9 -11
  77. data/lib/rails/generators/rails/app/templates/app/views/layouts/mailer.html.erb.tt +1 -1
  78. data/lib/rails/generators/rails/app/templates/bin/setup.tt +10 -1
  79. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +4 -17
  80. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +3 -3
  81. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +0 -2
  82. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +3 -3
  83. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +59 -0
  84. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +10 -2
  85. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +28 -24
  86. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +11 -7
  87. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +2 -0
  88. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +2 -2
  89. data/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt +1 -1
  90. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +223 -0
  91. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +11 -9
  92. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +11 -13
  93. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +10 -19
  94. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +4 -0
  95. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +6 -4
  96. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +10 -0
  97. data/lib/rails/generators/rails/app/templates/dockerignore.tt +43 -0
  98. data/lib/rails/generators/rails/app/templates/gitignore.tt +1 -9
  99. data/lib/rails/generators/rails/app/templates/node-version.tt +1 -0
  100. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +10 -8
  101. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +9 -7
  102. data/lib/rails/generators/rails/application_record/application_record_generator.rb +4 -0
  103. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +2 -1
  104. data/lib/rails/generators/rails/controller/USAGE +12 -4
  105. data/lib/rails/generators/rails/controller/controller_generator.rb +5 -0
  106. data/lib/rails/generators/rails/controller/templates/controller.rb.tt +1 -1
  107. data/lib/rails/generators/rails/credentials/credentials_generator.rb +29 -24
  108. data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +8 -0
  109. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +1 -2
  110. data/lib/rails/generators/rails/migration/USAGE +21 -11
  111. data/lib/rails/generators/rails/model/model_generator.rb +4 -0
  112. data/lib/rails/generators/rails/plugin/USAGE +17 -6
  113. data/lib/rails/generators/rails/plugin/plugin_generator.rb +5 -15
  114. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +2 -2
  115. data/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt +1 -1
  116. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +1 -17
  117. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +0 -2
  118. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +4 -4
  119. data/lib/rails/generators/rails/resource/resource_generator.rb +6 -0
  120. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +2 -1
  121. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +1 -1
  122. data/lib/rails/generators/test_case.rb +2 -2
  123. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +1 -1
  124. data/lib/rails/generators/testing/{behaviour.rb → behavior.rb} +4 -1
  125. data/lib/rails/generators.rb +6 -14
  126. data/lib/rails/health_controller.rb +55 -0
  127. data/lib/rails/info.rb +1 -1
  128. data/lib/rails/info_controller.rb +31 -11
  129. data/lib/rails/mailers_controller.rb +15 -5
  130. data/lib/rails/rack/logger.rb +15 -12
  131. data/lib/rails/rackup/server.rb +15 -0
  132. data/lib/rails/railtie/configuration.rb +14 -1
  133. data/lib/rails/railtie.rb +18 -18
  134. data/lib/rails/ruby_version_check.rb +2 -0
  135. data/lib/rails/source_annotation_extractor.rb +67 -18
  136. data/lib/rails/tasks/engine.rake +8 -8
  137. data/lib/rails/tasks/framework.rake +4 -10
  138. data/lib/rails/tasks/log.rake +1 -1
  139. data/lib/rails/tasks/misc.rake +3 -14
  140. data/lib/rails/tasks/statistics.rake +5 -4
  141. data/lib/rails/tasks/tmp.rake +5 -5
  142. data/lib/rails/tasks/zeitwerk.rake +1 -1
  143. data/lib/rails/tasks.rb +0 -2
  144. data/lib/rails/templates/rails/mailers/email.html.erb +25 -0
  145. data/lib/rails/templates/rails/mailers/index.html.erb +14 -7
  146. data/lib/rails/templates/rails/mailers/mailer.html.erb +11 -5
  147. data/lib/rails/templates/rails/welcome/index.html.erb +1 -0
  148. data/lib/rails/test_help.rb +7 -7
  149. data/lib/rails/test_unit/line_filtering.rb +1 -1
  150. data/lib/rails/test_unit/reporter.rb +6 -2
  151. data/lib/rails/test_unit/runner.rb +36 -18
  152. data/lib/rails/test_unit/test_parser.rb +88 -0
  153. data/lib/rails/test_unit/testing.rake +13 -33
  154. data/lib/rails/version.rb +1 -1
  155. data/lib/rails.rb +15 -15
  156. metadata +69 -31
  157. data/RDOC_MAIN.rdoc +0 -97
  158. data/lib/rails/application/dummy_erb_compiler.rb +0 -18
  159. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_0.rb.tt +0 -143
  160. data/lib/rails/generators/rails/model/USAGE +0 -113
  161. data/lib/rails/tasks/middleware.rake +0 -9
  162. data/lib/rails/tasks/restart.rake +0 -9
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/time/conversions"
4
- require "active_support/core_ext/object/blank"
5
4
  require "active_support/log_subscriber"
6
5
  require "rack/body_proxy"
7
6
 
@@ -22,7 +21,7 @@ module Rails
22
21
  request = ActionDispatch::Request.new(env)
23
22
 
24
23
  if logger.respond_to?(:tagged)
25
- logger.tagged(compute_tags(request)) { call_app(request, env) }
24
+ logger.tagged(*compute_tags(request)) { call_app(request, env) }
26
25
  else
27
26
  call_app(request, env)
28
27
  end
@@ -31,17 +30,21 @@ module Rails
31
30
  private
32
31
  def call_app(request, env) # :doc:
33
32
  instrumenter = ActiveSupport::Notifications.instrumenter
34
- instrumenter_state = instrumenter.start "request.action_dispatch", request: request
35
- instrumenter_finish = -> () {
36
- instrumenter.finish_with_state(instrumenter_state, "request.action_dispatch", request: request)
37
- }
33
+ handle = instrumenter.build_handle("request.action_dispatch", { request: request })
34
+ handle.start
38
35
 
39
36
  logger.info { started_request_message(request) }
40
- status, headers, body = @app.call(env)
41
- body = ::Rack::BodyProxy.new(body, &instrumenter_finish)
42
- [status, headers, body]
37
+ status, headers, body = response = @app.call(env)
38
+ body = ::Rack::BodyProxy.new(body, &handle.method(:finish))
39
+
40
+ if response.frozen?
41
+ [status, headers, body]
42
+ else
43
+ response[2] = body
44
+ response
45
+ end
43
46
  rescue Exception
44
- instrumenter_finish.call
47
+ handle.finish
45
48
  raise
46
49
  ensure
47
50
  ActiveSupport::LogSubscriber.flush_all!
@@ -49,11 +52,11 @@ module Rails
49
52
 
50
53
  # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
51
54
  def started_request_message(request) # :doc:
52
- 'Started %s "%s" for %s at %s' % [
55
+ sprintf('Started %s "%s" for %s at %s',
53
56
  request.raw_request_method,
54
57
  request.filtered_path,
55
58
  request.remote_ip,
56
- Time.now.to_default_s ]
59
+ Time.now)
57
60
  end
58
61
 
59
62
  def compute_tags(request) # :doc:
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :enddoc:
4
+
5
+ module Rails
6
+ module Rackup
7
+ begin
8
+ require "rackup/server"
9
+ Server = ::Rackup::Server
10
+ rescue LoadError
11
+ require "rack/server"
12
+ Server = ::Rack::Server
13
+ end
14
+ end
15
+ end
@@ -71,6 +71,11 @@ module Rails
71
71
  ActiveSupport.on_load(:after_initialize, yield: true, &block)
72
72
  end
73
73
 
74
+ # Called after application routes have been loaded.
75
+ def after_routes_loaded(&block)
76
+ ActiveSupport.on_load(:after_routes_loaded, yield: true, &block)
77
+ end
78
+
74
79
  # Array of callbacks defined by #to_prepare.
75
80
  def to_prepare_blocks
76
81
  @@to_prepare_blocks ||= []
@@ -87,9 +92,17 @@ module Rails
87
92
  end
88
93
 
89
94
  private
95
+ def actual_method?(key)
96
+ !@@options.key?(key) && respond_to?(key)
97
+ end
98
+
90
99
  def method_missing(name, *args, &blk)
91
100
  if name.end_with?("=")
92
- @@options[:"#{name[0..-2]}"] = args.first
101
+ key = name[0..-2].to_sym
102
+ if actual_method?(key)
103
+ raise NoMethodError.new("Cannot assign to `#{key}`, it is a configuration method")
104
+ end
105
+ @@options[key] = args.first
93
106
  elsif @@options.key?(name)
94
107
  @@options[name]
95
108
  else
data/lib/rails/railtie.rb CHANGED
@@ -7,34 +7,34 @@ require "active_support/core_ext/module/introspection"
7
7
  require "active_support/core_ext/module/delegation"
8
8
 
9
9
  module Rails
10
- # <tt>Rails::Railtie</tt> is the core of the Rails framework and provides
11
- # several hooks to extend Rails and/or modify the initialization process.
10
+ # +Rails::Railtie+ is the core of the \Rails framework and provides
11
+ # several hooks to extend \Rails and/or modify the initialization process.
12
12
  #
13
- # Every major component of Rails (Action Mailer, Action Controller, Active
13
+ # Every major component of \Rails (Action Mailer, Action Controller, Active
14
14
  # Record, etc.) implements a railtie. Each of them is responsible for their
15
- # own initialization. This makes Rails itself absent of any component hooks,
16
- # allowing other components to be used in place of any of the Rails defaults.
15
+ # own initialization. This makes \Rails itself absent of any component hooks,
16
+ # allowing other components to be used in place of any of the \Rails defaults.
17
17
  #
18
- # Developing a Rails extension does _not_ require implementing a railtie, but
19
- # if you need to interact with the Rails framework during or after boot, then
18
+ # Developing a \Rails extension does _not_ require implementing a railtie, but
19
+ # if you need to interact with the \Rails framework during or after boot, then
20
20
  # a railtie is needed.
21
21
  #
22
22
  # For example, an extension doing any of the following would need a railtie:
23
23
  #
24
24
  # * creating initializers
25
- # * configuring a Rails framework for the application, like setting a generator
25
+ # * configuring a \Rails framework for the application, like setting a generator
26
26
  # * adding <tt>config.*</tt> keys to the environment
27
27
  # * setting up a subscriber with ActiveSupport::Notifications
28
28
  # * adding Rake tasks
29
29
  #
30
30
  # == Creating a Railtie
31
31
  #
32
- # To extend Rails using a railtie, create a subclass of <tt>Rails::Railtie</tt>.
33
- # This class must be loaded during the Rails boot process, and is conventionally
34
- # called <tt>MyNamespace::Railtie</tt>.
32
+ # To extend \Rails using a railtie, create a subclass of +Rails::Railtie+.
33
+ # This class must be loaded during the \Rails boot process, and is conventionally
34
+ # called +MyNamespace::Railtie+.
35
35
  #
36
36
  # The following example demonstrates an extension which can be used with or
37
- # without Rails.
37
+ # without \Rails.
38
38
  #
39
39
  # # lib/my_gem/railtie.rb
40
40
  # module MyGem
@@ -47,7 +47,7 @@ module Rails
47
47
  #
48
48
  # == Initializers
49
49
  #
50
- # To add an initialization step to the Rails boot process from your railtie, just
50
+ # To add an initialization step to the \Rails boot process from your railtie, just
51
51
  # define the initialization code with the +initializer+ macro:
52
52
  #
53
53
  # class MyRailtie < Rails::Railtie
@@ -87,7 +87,7 @@ module Rails
87
87
  #
88
88
  # == Loading Rake Tasks and Generators
89
89
  #
90
- # If your railtie has Rake tasks, you can tell Rails to load them through the method
90
+ # If your railtie has Rake tasks, you can tell \Rails to load them through the method
91
91
  # +rake_tasks+:
92
92
  #
93
93
  # class MyRailtie < Rails::Railtie
@@ -96,7 +96,7 @@ module Rails
96
96
  # end
97
97
  # end
98
98
  #
99
- # By default, Rails loads generators from your load path. However, if you want to place
99
+ # By default, \Rails loads generators from your load path. However, if you want to place
100
100
  # your generators at a different location, you can specify in your railtie a block which
101
101
  # will load them during normal generators lookup:
102
102
  #
@@ -109,13 +109,13 @@ module Rails
109
109
  # Since filenames on the load path are shared across gems, be sure that files you load
110
110
  # through a railtie have unique names.
111
111
  #
112
- # == Run another program when the Rails server starts
112
+ # == Run another program when the \Rails server starts
113
113
  #
114
- # In development, it's very usual to have to run another process next to the Rails Server. In example
114
+ # In development, it's very usual to have to run another process next to the \Rails Server. In example
115
115
  # you might want to start the Webpack or React server. Or maybe you need to run your job scheduler process
116
116
  # like Sidekiq. This is usually done by opening a new shell and running the program from here.
117
117
  #
118
- # Rails allow you to specify a +server+ block which will get called when a Rails server starts.
118
+ # \Rails allow you to specify a +server+ block which will get called when a \Rails server starts.
119
119
  # This way, your users don't need to remember to have to open a new shell and run another program, making
120
120
  # this less confusing for everyone.
121
121
  # It can be used like this:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :stopdoc:
4
+
3
5
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7.0") && RUBY_ENGINE == "ruby"
4
6
  desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
5
7
  abort <<-end_message
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ripper"
4
+
3
5
  module Rails
4
- # Implements the logic behind <tt>Rails::Command::NotesCommand</tt>. See <tt>rails notes --help</tt> for usage information.
6
+ # Implements the logic behind +Rails::Command::NotesCommand+. See <tt>rails notes --help</tt> for usage information.
5
7
  #
6
8
  # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
7
9
  # represent the line where the annotation lives, its tag, and its text. Note
@@ -11,6 +13,44 @@ module Rails
11
13
  # start with the tag optionally followed by a colon. Everything up to the end
12
14
  # of the line (or closing ERB comment tag) is considered to be their text.
13
15
  class SourceAnnotationExtractor
16
+ # Wraps a regular expression that will be tested against each of the source
17
+ # file's comments.
18
+ class ParserExtractor < Struct.new(:pattern)
19
+ class Parser < Ripper
20
+ attr_reader :comments, :pattern
21
+
22
+ def initialize(source, pattern:)
23
+ super(source)
24
+ @pattern = pattern
25
+ @comments = []
26
+ end
27
+
28
+ def on_comment(value)
29
+ @comments << Annotation.new(lineno, $1, $2) if value =~ pattern
30
+ end
31
+ end
32
+
33
+ def annotations(file)
34
+ contents = File.read(file, encoding: Encoding::BINARY)
35
+ parser = Parser.new(contents, pattern: pattern).tap(&:parse)
36
+ parser.error? ? [] : parser.comments
37
+ end
38
+ end
39
+
40
+ # Wraps a regular expression that will iterate through a file's lines and
41
+ # test each one for the given pattern.
42
+ class PatternExtractor < Struct.new(:pattern)
43
+ def annotations(file)
44
+ lineno = 0
45
+
46
+ File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line|
47
+ lineno += 1
48
+ next list unless line =~ pattern
49
+ list << Annotation.new(lineno, $1, $2)
50
+ end
51
+ end
52
+ end
53
+
14
54
  class Annotation < Struct.new(:line, :tag, :text)
15
55
  def self.directories
16
56
  @@directories ||= %w(app config db lib test)
@@ -42,9 +82,21 @@ module Rails
42
82
  extensions[/\.(#{exts.join("|")})$/] = block
43
83
  end
44
84
 
45
- register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ }
46
- register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
47
- register_extensions("erb") { |tag| /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ }
85
+ register_extensions("builder", "rb", "rake", "ruby") do |tag|
86
+ ParserExtractor.new(/#\s*(#{tag}):?\s*(.*)$/)
87
+ end
88
+
89
+ register_extensions("yml", "yaml") do |tag|
90
+ PatternExtractor.new(/#\s*(#{tag}):?\s*(.*)$/)
91
+ end
92
+
93
+ register_extensions("css", "js") do |tag|
94
+ PatternExtractor.new(/\/\/\s*(#{tag}):?\s*(.*)$/)
95
+ end
96
+
97
+ register_extensions("erb") do |tag|
98
+ PatternExtractor.new(/<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/)
99
+ end
48
100
 
49
101
  # Returns a representation of the annotation that looks like this:
50
102
  #
@@ -111,7 +163,17 @@ module Rails
111
163
 
112
164
  if extension
113
165
  pattern = extension.last.call(tag)
114
- results.update(extract_annotations_from(item, pattern)) if pattern
166
+
167
+ # In case a user-defined pattern returns nothing for the given set
168
+ # of tags, we exit early.
169
+ next unless pattern
170
+
171
+ # If a user-defined pattern returns a regular expression, we will
172
+ # wrap it in a PatternExtractor to keep the same API.
173
+ pattern = PatternExtractor.new(pattern) if pattern.is_a?(Regexp)
174
+
175
+ annotations = pattern.annotations(item)
176
+ results.update(item => annotations) if annotations.any?
115
177
  end
116
178
  end
117
179
  end
@@ -119,19 +181,6 @@ module Rails
119
181
  results
120
182
  end
121
183
 
122
- # If +file+ is the filename of a file that contains annotations this method returns
123
- # a hash with a single entry that maps +file+ to an array of its annotations.
124
- # Otherwise it returns an empty hash.
125
- def extract_annotations_from(file, pattern)
126
- lineno = 0
127
- result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line|
128
- lineno += 1
129
- next list unless line =~ pattern
130
- list << Annotation.new(lineno, $1, $2)
131
- end
132
- result.empty? ? {} : { file => result }
133
- end
134
-
135
184
  # Prints the mapping from filenames to annotations in +results+ ordered by filename.
136
185
  # The +options+ hash is passed to each annotation's +to_s+.
137
186
  def display(results, options = {})
@@ -18,7 +18,7 @@ task "load_app" do
18
18
  task environment: "app:environment"
19
19
 
20
20
  if !defined?(ENGINE_ROOT) || !ENGINE_ROOT
21
- ENGINE_ROOT = find_engine_path(APP_RAKEFILE)
21
+ ENGINE_ROOT = find_engine_path(Pathname.new(APP_RAKEFILE))
22
22
  end
23
23
  end
24
24
 
@@ -43,17 +43,17 @@ namespace :db do
43
43
  app_task "create"
44
44
  app_task "create:all"
45
45
 
46
- desc "Drops the database for the current Rails.env (use db:drop:all to drop all databases)"
46
+ desc "Drop the database for the current Rails.env (use db:drop:all to drop all databases)"
47
47
  app_task "drop"
48
48
  app_task "drop:all"
49
49
 
50
50
  desc "Load fixtures into the current environment's database."
51
51
  app_task "fixtures:load"
52
52
 
53
- desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)."
53
+ desc "Roll the schema back to the previous version (specify steps w/ STEP=n)."
54
54
  app_task "rollback"
55
55
 
56
- desc "Creates a database schema file (either db/schema.rb or db/structure.sql, depending on `config.active_record.schema_format`)"
56
+ desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on `config.active_record.schema_format`)"
57
57
  app_task "schema:dump"
58
58
 
59
59
  desc "Load a schema.rb file into the database"
@@ -65,7 +65,7 @@ namespace :db do
65
65
  desc "Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the database first)"
66
66
  app_task "setup"
67
67
 
68
- desc "Retrieves the current schema version number"
68
+ desc "Retrieve the current schema version number"
69
69
  app_task "version"
70
70
 
71
71
  # desc 'Load the test schema'
@@ -73,12 +73,12 @@ namespace :db do
73
73
  end
74
74
 
75
75
  def find_engine_path(path)
76
- return File.expand_path(Dir.pwd) if path == "/"
76
+ return File.expand_path(Dir.pwd) if path.root?
77
77
 
78
78
  if Rails::Engine.find(path)
79
- path
79
+ path.to_s
80
80
  else
81
- find_engine_path(File.expand_path("..", path))
81
+ find_engine_path(path.join(".."))
82
82
  end
83
83
  end
84
84
 
@@ -2,17 +2,15 @@
2
2
 
3
3
  namespace :app do
4
4
  desc "Update configs and some other initially generated files (or use just update:configs or update:bin)"
5
- task update: [ "update:configs", "update:bin", "update:db", "update:active_storage", "update:upgrade_guide_info" ]
5
+ task update: [ "update:configs", "update:bin", "update:active_storage", "update:upgrade_guide_info" ]
6
6
 
7
- desc "Applies the template supplied by LOCATION=(/path/to/template) or URL"
7
+ desc "Apply the template supplied by LOCATION=(/path/to/template) or URL"
8
8
  task template: :environment do
9
9
  template = ENV["LOCATION"]
10
10
  raise "No LOCATION value given. Please set LOCATION either as path to a file or a URL" if template.blank?
11
- template = File.expand_path(template) unless %r{\A[A-Za-z][A-Za-z0-9+\-.]*://}.match?(template)
12
11
  require "rails/generators"
13
12
  require "rails/generators/rails/app/app_generator"
14
- generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root }
15
- generator.apply template, verbose: false
13
+ Rails::Generators::AppGenerator.apply_rails_template(template, Rails.root)
16
14
  end
17
15
 
18
16
  namespace :templates do
@@ -46,15 +44,11 @@ namespace :app do
46
44
  Rails::AppUpdater.invoke_from_app_generator :update_config_files
47
45
  end
48
46
 
49
- # desc "Adds new executables to the application bin/ directory"
47
+ # desc "Add new executables to the application bin/ directory"
50
48
  task :bin do
51
49
  Rails::AppUpdater.invoke_from_app_generator :update_bin_files
52
50
  end
53
51
 
54
- task :db do
55
- Rails::AppUpdater.invoke_from_app_generator :update_db_schema
56
- end
57
-
58
52
  task :active_storage do
59
53
  Rails::AppUpdater.invoke_from_app_generator :update_active_storage
60
54
  end
@@ -7,7 +7,7 @@ namespace :log do
7
7
  # - defaults to all environments log files i.e. 'development,test,production'
8
8
  # - ENV['LOGS']=all truncates all files i.e. log/*.log
9
9
  # - ENV['LOGS']='test,development' truncates only specified files
10
- desc "Truncates all/specified *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)"
10
+ desc "Truncate all/specified *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)"
11
11
  task :clear do
12
12
  log_files.each do |file|
13
13
  clear_log_file(file)
@@ -1,16 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- desc "Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions)."
4
- task :secret do
5
- require "securerandom"
6
- puts SecureRandom.hex(64)
7
- end
8
-
9
- desc "List versions of all Rails frameworks and the environment"
10
- task about: :environment do
11
- puts Rails::Info
12
- end
13
-
14
3
  namespace :time do
15
4
  desc "List all time zones, list by two-letter country code (`bin/rails time:zones[US]`), or list by UTC offset (`bin/rails time:zones[-8]`)"
16
5
  task :zones, :country_or_offset do |t, args|
@@ -28,17 +17,17 @@ namespace :time do
28
17
  end
29
18
 
30
19
  namespace :zones do
31
- # desc 'Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6'
20
+ # desc 'Display all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6'
32
21
  task :all do
33
22
  build_time_zone_list ActiveSupport::TimeZone.all
34
23
  end
35
24
 
36
- # desc 'Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
25
+ # desc 'Display names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
37
26
  task :us do
38
27
  build_time_zone_list ActiveSupport::TimeZone.us_zones
39
28
  end
40
29
 
41
- # desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time'
30
+ # desc 'Display names of time zones recognized by the Rails TimeZone class with the same offset as the system local time'
42
31
  task :local do
43
32
  require "active_support"
44
33
  require "active_support/time"
@@ -26,12 +26,13 @@ STATS_DIRECTORIES ||= [
26
26
  %w(Channel\ tests test/channels),
27
27
  %w(Integration\ tests test/integration),
28
28
  %w(System\ tests test/system),
29
- ].collect do |name, dir|
30
- [ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
31
- end.select { |name, dir| File.directory?(dir) }
29
+ ]
32
30
 
33
31
  desc "Report code statistics (KLOCs, etc) from the application or engine"
34
32
  task :stats do
35
33
  require "rails/code_statistics"
36
- CodeStatistics.new(*STATS_DIRECTORIES).to_s
34
+ stat_directories = STATS_DIRECTORIES.collect do |name, dir|
35
+ [ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
36
+ end.select { |name, dir| File.directory?(dir) }
37
+ CodeStatistics.new(*stat_directories).to_s
37
38
  end
@@ -11,32 +11,32 @@ namespace :tmp do
11
11
 
12
12
  tmp_dirs.each { |d| directory d }
13
13
 
14
- desc "Creates tmp directories for cache, sockets, and pids"
14
+ desc "Create tmp directories for cache, sockets, and pids"
15
15
  task create: tmp_dirs
16
16
 
17
17
  namespace :cache do
18
- # desc "Clears all files and directories in tmp/cache"
18
+ # desc "Clear all files and directories in tmp/cache"
19
19
  task :clear do
20
20
  rm_rf Dir["tmp/cache/[^.]*"], verbose: false
21
21
  end
22
22
  end
23
23
 
24
24
  namespace :sockets do
25
- # desc "Clears all files in tmp/sockets"
25
+ # desc "Clear all files in tmp/sockets"
26
26
  task :clear do
27
27
  rm Dir["tmp/sockets/[^.]*"], verbose: false
28
28
  end
29
29
  end
30
30
 
31
31
  namespace :pids do
32
- # desc "Clears all files in tmp/pids"
32
+ # desc "Clear all files in tmp/pids"
33
33
  task :clear do
34
34
  rm Dir["tmp/pids/[^.]*"], verbose: false
35
35
  end
36
36
  end
37
37
 
38
38
  namespace :screenshots do
39
- # desc "Clears all files in tmp/screenshots"
39
+ # desc "Clear all files in tmp/screenshots"
40
40
  task :clear do
41
41
  rm Dir["tmp/screenshots/[^.]*"], verbose: false
42
42
  end
@@ -33,7 +33,7 @@ report = ->(not_checked) do
33
33
  end
34
34
 
35
35
  namespace :zeitwerk do
36
- desc "Checks project structure for Zeitwerk compatibility"
36
+ desc "Check project structure for Zeitwerk compatibility"
37
37
  task check: :environment do
38
38
  begin
39
39
  eager_load[]
data/lib/rails/tasks.rb CHANGED
@@ -6,9 +6,7 @@ require "rake"
6
6
  %w(
7
7
  framework
8
8
  log
9
- middleware
10
9
  misc
11
- restart
12
10
  tmp
13
11
  yarn
14
12
  zeitwerk
@@ -80,6 +80,11 @@
80
80
  <dd id="cc"><%= @email.header['cc'] %></dd>
81
81
  <% end %>
82
82
 
83
+ <% if @email.bcc %>
84
+ <dt>BCC:</dt>
85
+ <dd id="bcc"><%= @email.header['bcc'] %></dd>
86
+ <% end %>
87
+
83
88
  <dt>Date:</dt>
84
89
  <dd id="date"><%= Time.current.rfc2822 %></dd>
85
90
 
@@ -120,6 +125,26 @@
120
125
  </select>
121
126
  </dd>
122
127
  <% end %>
128
+
129
+ <% unless @email.header_fields.nil? || @email.header_fields.empty? %>
130
+ <dt>Headers:</dt>
131
+ <dd>
132
+ <details>
133
+ <summary>Show all headers</summary>
134
+ <table>
135
+ <% @email.header_fields.each do |field| %>
136
+ <tr>
137
+ <td align="right" style="color: #7f7f7f"><%= field.name %>:</td>
138
+ <td><%= field.value %></td>
139
+ </tr>
140
+ <% end %>
141
+ </table>
142
+ </details>
143
+ </dd>
144
+ <% end %>
145
+
146
+ <dt>EML File:</dt>
147
+ <dd><%= link_to "Download", action: :download %></dd>
123
148
  </dl>
124
149
  </header>
125
150
 
@@ -1,8 +1,15 @@
1
- <% @previews.each do |preview| %>
2
- <h3><%= link_to preview.preview_name.titleize, url_for(controller: "rails/mailers", action: "preview", path: preview.preview_name) %></h3>
3
- <ul>
4
- <% preview.emails.each do |email| %>
5
- <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{preview.preview_name}/#{email}") %></li>
6
- <% end %>
7
- </ul>
1
+ <h1><%= @page_title %></h1>
2
+
3
+ <% if @previews.any? %>
4
+ <% @previews.each do |preview| %>
5
+ <h3><%= link_to preview.preview_name.titleize, url_for(controller: "rails/mailers", action: "preview", path: preview.preview_name) %></h3>
6
+ <ul>
7
+ <% preview.emails.each do |email| %>
8
+ <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{preview.preview_name}/#{email}") %></li>
9
+ <% end %>
10
+ </ul>
11
+ <% end %>
12
+ <% else %>
13
+ <p>You have not defined any Action Mailer Previews.</p>
14
+ <p>Read <%= link_to "Action Mailer Basics", "https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails" %> to learn how to define your first.</p>
8
15
  <% end %>
@@ -1,6 +1,12 @@
1
- <h3><%= @preview.preview_name.titleize %></h3>
2
- <ul>
3
- <% @preview.emails.each do |email| %>
4
- <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{@preview.preview_name}/#{email}") %></li>
1
+ <h1><%= @page_title %></h1>
2
+
3
+ <% if @preview.emails.any? %>
4
+ <ul>
5
+ <% @preview.emails.each do |email| %>
6
+ <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{@preview.preview_name}/#{email}") %></li>
7
+ <% end %>
8
+ </ul>
9
+ <% else %>
10
+ <p>You have not defined any actions for <%= @preview %>.</p>
11
+ <p>Read <%= link_to "Action Mailer Basics", "https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails" %> to learn how to define your first.</p>
5
12
  <% end %>
6
- </ul>
@@ -50,6 +50,7 @@
50
50
  border-radius: 100%;
51
51
  display: flex;
52
52
  transition: background 0.25s cubic-bezier(0.33, 1, 0.68, 1);
53
+ filter: drop-shadow(0 20px 13px rgb(0 0 0 / 0.03)) drop-shadow(0 8px 5px rgb(0 0 0 / 0.08));
53
54
  }
54
55
 
55
56
  nav a:hover {