railties 5.2.3.rc1 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of railties might be problematic. Click here for more details.

Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +190 -130
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.rdoc +35 -28
  5. data/README.rdoc +1 -1
  6. data/lib/minitest/rails_plugin.rb +6 -10
  7. data/lib/rails/all.rb +4 -0
  8. data/lib/rails/api/generator.rb +2 -1
  9. data/lib/rails/api/task.rb +16 -0
  10. data/lib/rails/app_loader.rb +1 -1
  11. data/lib/rails/app_updater.rb +3 -1
  12. data/lib/rails/application.rb +21 -45
  13. data/lib/rails/application/configuration.rb +54 -12
  14. data/lib/rails/application/default_middleware_stack.rb +2 -0
  15. data/lib/rails/backtrace_cleaner.rb +5 -17
  16. data/lib/rails/code_statistics.rb +3 -3
  17. data/lib/rails/command.rb +11 -10
  18. data/lib/rails/command/actions.rb +0 -10
  19. data/lib/rails/command/base.rb +1 -1
  20. data/lib/rails/command/behavior.rb +4 -46
  21. data/lib/rails/command/environment_argument.rb +1 -11
  22. data/lib/rails/command/spellchecker.rb +58 -0
  23. data/lib/rails/commands/credentials/USAGE +19 -1
  24. data/lib/rails/commands/credentials/credentials_command.rb +42 -23
  25. data/lib/rails/commands/db/system/change/change_command.rb +20 -0
  26. data/lib/rails/commands/dbconsole/dbconsole_command.rb +2 -2
  27. data/lib/rails/commands/dev/dev_command.rb +17 -0
  28. data/lib/rails/commands/encrypted/encrypted_command.rb +2 -3
  29. data/lib/rails/commands/help/help_command.rb +1 -1
  30. data/lib/rails/commands/initializers/initializers_command.rb +16 -0
  31. data/lib/rails/commands/new/new_command.rb +2 -2
  32. data/lib/rails/commands/notes/notes_command.rb +39 -0
  33. data/lib/rails/commands/plugin/plugin_command.rb +1 -1
  34. data/lib/rails/commands/routes/routes_command.rb +37 -0
  35. data/lib/rails/commands/runner/runner_command.rb +6 -6
  36. data/lib/rails/commands/secrets/USAGE +3 -3
  37. data/lib/rails/commands/secrets/secrets_command.rb +3 -3
  38. data/lib/rails/commands/server/server_command.rb +109 -48
  39. data/lib/rails/configuration.rb +1 -7
  40. data/lib/rails/engine/configuration.rb +3 -1
  41. data/lib/rails/gem_version.rb +4 -4
  42. data/lib/rails/generators.rb +11 -12
  43. data/lib/rails/generators/actions.rb +48 -37
  44. data/lib/rails/generators/app_base.rb +56 -94
  45. data/lib/rails/generators/app_name.rb +50 -0
  46. data/lib/rails/generators/database.rb +57 -0
  47. data/lib/rails/generators/erb/mailer/mailer_generator.rb +1 -1
  48. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +1 -1
  49. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +1 -1
  50. data/lib/rails/generators/generated_attribute.rb +17 -17
  51. data/lib/rails/generators/model_helpers.rb +8 -1
  52. data/lib/rails/generators/named_base.rb +1 -5
  53. data/lib/rails/generators/rails/app/app_generator.rb +37 -71
  54. data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -6
  55. data/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt +0 -3
  56. data/lib/rails/generators/rails/app/templates/app/{assets/javascripts/cable.js.tt → javascript/channels/consumer.js} +2 -9
  57. data/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js +5 -0
  58. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +15 -0
  59. data/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt +5 -0
  60. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +4 -4
  61. data/lib/rails/generators/rails/app/templates/bin/setup.tt +4 -5
  62. data/lib/rails/generators/rails/app/templates/bin/update.tt +6 -7
  63. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +2 -0
  64. data/lib/rails/generators/rails/app/templates/config/cable.yml.tt +1 -1
  65. data/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt +2 -2
  66. data/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt +2 -2
  67. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +2 -2
  68. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +3 -3
  69. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +3 -3
  70. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +4 -4
  71. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +2 -2
  72. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +6 -6
  73. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +2 -2
  74. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +3 -2
  75. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +5 -12
  76. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +6 -2
  77. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +1 -1
  78. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
  79. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt +33 -0
  80. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +1 -1
  81. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
  82. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +1 -1
  83. data/lib/rails/generators/rails/app/templates/config/spring.rb.tt +6 -6
  84. data/lib/rails/generators/rails/app/templates/gitignore.tt +2 -7
  85. data/lib/rails/generators/rails/app/templates/package.json.tt +7 -1
  86. data/lib/rails/generators/rails/app/templates/ruby-version.tt +1 -1
  87. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +11 -0
  88. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +7 -0
  89. data/lib/rails/generators/rails/assets/USAGE +1 -4
  90. data/lib/rails/generators/rails/assets/assets_generator.rb +0 -8
  91. data/lib/rails/generators/rails/controller/controller_generator.rb +11 -1
  92. data/lib/rails/generators/rails/credentials/credentials_generator.rb +7 -8
  93. data/lib/rails/generators/rails/db/system/change/change_generator.rb +55 -0
  94. data/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb +4 -5
  95. data/lib/rails/generators/rails/helper/helper_generator.rb +5 -0
  96. data/lib/rails/generators/rails/plugin/plugin_generator.rb +9 -18
  97. data/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt +1 -1
  98. data/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt +1 -1
  99. data/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt +1 -1
  100. data/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt +1 -1
  101. data/lib/rails/generators/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt +1 -1
  102. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +1 -1
  103. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt +1 -1
  104. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt +1 -1
  105. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +1 -2
  106. data/lib/rails/generators/resource_helpers.rb +1 -6
  107. data/lib/rails/generators/test_unit/integration/integration_generator.rb +6 -0
  108. data/lib/rails/generators/test_unit/job/job_generator.rb +5 -0
  109. data/lib/rails/generators/test_unit/mailer/mailer_generator.rb +1 -1
  110. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +1 -1
  111. data/lib/rails/generators/test_unit/system/system_generator.rb +5 -0
  112. data/lib/rails/generators/testing/behaviour.rb +3 -0
  113. data/lib/rails/info.rb +2 -2
  114. data/lib/rails/info_controller.rb +1 -1
  115. data/lib/rails/mailers_controller.rb +1 -1
  116. data/lib/rails/paths.rb +19 -9
  117. data/lib/rails/railtie.rb +1 -1
  118. data/lib/rails/ruby_version_check.rb +3 -3
  119. data/lib/rails/secrets.rb +0 -1
  120. data/lib/rails/source_annotation_extractor.rb +125 -117
  121. data/lib/rails/tasks/annotations.rake +9 -9
  122. data/lib/rails/tasks/dev.rake +5 -4
  123. data/lib/rails/tasks/framework.rake +5 -1
  124. data/lib/rails/tasks/initializers.rake +5 -4
  125. data/lib/rails/tasks/log.rake +0 -1
  126. data/lib/rails/tasks/routes.rake +4 -26
  127. data/lib/rails/tasks/statistics.rake +1 -0
  128. data/lib/rails/tasks/yarn.rake +1 -1
  129. data/lib/rails/templates/rails/welcome/index.html.erb +2 -2
  130. data/lib/rails/test_help.rb +11 -9
  131. data/lib/rails/test_unit/reporter.rb +1 -1
  132. data/lib/rails/test_unit/runner.rb +5 -5
  133. data/lib/rails/test_unit/testing.rake +1 -1
  134. metadata +27 -21
  135. data/lib/rails/commands/encrypted/USAGE +0 -28
  136. data/lib/rails/generators/js/assets/assets_generator.rb +0 -15
  137. data/lib/rails/generators/js/assets/templates/javascript.js +0 -2
  138. data/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +0 -22
  139. data/lib/rails/generators/rails/app/templates/bin/bundle.tt +0 -2
  140. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt +0 -38
  141. data/lib/rails/generators/rails/assets/templates/javascript.js +0 -2
@@ -1,4 +1,4 @@
1
- <%= wrap_in_modules <<-rb.strip_heredoc
1
+ <%= wrap_in_modules <<~rb
2
2
  class ApplicationController < ActionController::#{api? ? "API" : "Base"}
3
3
  #{ api? ? '# ' : '' }protect_from_forgery with: :exception
4
4
  end
@@ -1,4 +1,4 @@
1
- <%= wrap_in_modules <<-rb.strip_heredoc
1
+ <%= wrap_in_modules <<~rb
2
2
  module ApplicationHelper
3
3
  end
4
4
  rb
@@ -1,4 +1,4 @@
1
- <%= wrap_in_modules <<-rb.strip_heredoc
1
+ <%= wrap_in_modules <<~rb
2
2
  class ApplicationJob < ActiveJob::Base
3
3
  end
4
4
  rb
@@ -1,4 +1,4 @@
1
- <%= wrap_in_modules <<-rb.strip_heredoc
1
+ <%= wrap_in_modules <<~rb
2
2
  class ApplicationMailer < ActionMailer::Base
3
3
  default from: 'from@example.com'
4
4
  layout 'mailer'
@@ -1,4 +1,4 @@
1
- <%= wrap_in_modules <<-rb.strip_heredoc
1
+ <%= wrap_in_modules <<~rb
2
2
  class ApplicationRecord < ActiveRecord::Base
3
3
  self.abstract_class = true
4
4
  end
@@ -7,7 +7,7 @@ pkg/
7
7
  <%= dummy_path %>/db/*.sqlite3-journal
8
8
  <% end -%>
9
9
  <%= dummy_path %>/log/*.log
10
- <% unless options[:skip_yarn] -%>
10
+ <% unless options[:skip_javascript] -%>
11
11
  <%= dummy_path %>/node_modules/
12
12
  <%= dummy_path %>/yarn-error.log
13
13
  <% end -%>
@@ -1,4 +1,4 @@
1
- <%= wrap_in_modules <<-rb.strip_heredoc
1
+ <%= wrap_in_modules <<~rb
2
2
  class Engine < ::Rails::Engine
3
3
  #{mountable? ? ' isolate_namespace ' + camelized_modules : ' '}
4
4
  #{api? ? " config.generators.api_only = true" : ' '}
@@ -1,4 +1,4 @@
1
- <%= wrap_in_modules <<-rb.strip_heredoc
1
+ <%= wrap_in_modules <<~rb
2
2
  class Railtie < ::Rails::Railtie
3
3
  end
4
4
  rb
@@ -10,8 +10,7 @@ ActiveRecord::Migrator.migrations_paths << File.expand_path('../db/migrate', __d
10
10
  <% end -%>
11
11
  require "rails/test_help"
12
12
 
13
- # Filter out Minitest backtrace while allowing backtrace from other libraries
14
- # to be shown.
13
+ # Filter out the backtrace from minitest while preserving the one from other libraries.
15
14
  Minitest.backtrace_filter = Minitest::BacktraceFilter.new
16
15
 
17
16
  <% unless engine? -%>
@@ -25,13 +25,8 @@ module Rails
25
25
  assign_controller_names!(controller_name.pluralize)
26
26
  end
27
27
 
28
- # TODO Change this to private once we've dropped Ruby 2.2 support.
29
- # Workaround for Ruby 2.2 "private attribute?" warning.
30
- protected
31
-
32
- attr_reader :controller_name, :controller_file_name
33
-
34
28
  private
29
+ attr_reader :controller_name, :controller_file_name
35
30
 
36
31
  def controller_class_path
37
32
  if options[:model_name]
@@ -10,6 +10,12 @@ module TestUnit # :nodoc:
10
10
  def create_test_files
11
11
  template "integration_test.rb", File.join("test/integration", class_path, "#{file_name}_test.rb")
12
12
  end
13
+
14
+ private
15
+
16
+ def file_name
17
+ @_file_name ||= super.sub(/_test\z/i, "")
18
+ end
13
19
  end
14
20
  end
15
21
  end
@@ -10,6 +10,11 @@ module TestUnit # :nodoc:
10
10
  def create_test_file
11
11
  template "unit_test.rb", File.join("test/jobs", class_path, "#{file_name}_job_test.rb")
12
12
  end
13
+
14
+ private
15
+ def file_name
16
+ @_file_name ||= super.sub(/_job\z/i, "")
17
+ end
13
18
  end
14
19
  end
15
20
  end
@@ -21,7 +21,7 @@ module TestUnit # :nodoc:
21
21
 
22
22
  private
23
23
  def file_name
24
- @_file_name ||= super.gsub(/_mailer/i, "")
24
+ @_file_name ||= super.sub(/_mailer\z/i, "")
25
25
  end
26
26
  end
27
27
  end
@@ -57,7 +57,7 @@ module TestUnit # :nodoc:
57
57
 
58
58
  def boolean?(name)
59
59
  attribute = attributes.find { |attr| attr.name == name }
60
- attribute && attribute.type == :boolean
60
+ attribute&.type == :boolean
61
61
  end
62
62
  end
63
63
  end
@@ -14,6 +14,11 @@ module TestUnit # :nodoc:
14
14
 
15
15
  template "system_test.rb", File.join("test/system", class_path, "#{file_name.pluralize}_test.rb")
16
16
  end
17
+
18
+ private
19
+ def file_name
20
+ @_file_name ||= super.sub(/_test\z/i, "")
21
+ end
17
22
  end
18
23
  end
19
24
  end
@@ -67,6 +67,9 @@ module Rails
67
67
  def run_generator(args = default_arguments, config = {})
68
68
  capture(:stdout) do
69
69
  args += ["--skip-bundle"] unless args.include? "--dev"
70
+ args |= ["--skip-bootsnap"] unless args.include? "--no-skip-bootsnap"
71
+ args |= ["--skip-webpack-install"] unless args.include? "--no-skip-webpack-install"
72
+
70
73
  generator_class.start(args, config.reverse_merge(destination_root: destination_root))
71
74
  end
72
75
  end
@@ -41,7 +41,7 @@ module Rails
41
41
  alias inspect to_s
42
42
 
43
43
  def to_html
44
- "<table>".dup.tap do |table|
44
+ (+"<table>").tap do |table|
45
45
  properties.each do |(name, value)|
46
46
  table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
47
47
  formatted_value = if value.kind_of?(Array)
@@ -63,7 +63,7 @@ module Rails
63
63
 
64
64
  # The Ruby version and platform, e.g. "2.0.0-p247 (x86_64-darwin12.4.0)".
65
65
  property "Ruby version" do
66
- "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
66
+ RUBY_DESCRIPTION
67
67
  end
68
68
 
69
69
  # The RubyGems version, if it's installed.
@@ -4,7 +4,7 @@ require "rails/application_controller"
4
4
  require "action_dispatch/routing/inspector"
5
5
 
6
6
  class Rails::InfoController < Rails::ApplicationController # :nodoc:
7
- prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
7
+ prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
8
8
  layout -> { request.xhr? ? false : "application" }
9
9
 
10
10
  before_action :require_local!
@@ -3,7 +3,7 @@
3
3
  require "rails/application_controller"
4
4
 
5
5
  class Rails::MailersController < Rails::ApplicationController # :nodoc:
6
- prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
6
+ prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
7
7
 
8
8
  before_action :require_local!, unless: :show_previews?
9
9
  before_action :find_preview, :set_locale, only: :preview
@@ -113,10 +113,11 @@ module Rails
113
113
  attr_accessor :glob
114
114
 
115
115
  def initialize(root, current, paths, options = {})
116
- @paths = paths
117
- @current = current
118
- @root = root
119
- @glob = options[:glob]
116
+ @paths = paths
117
+ @current = current
118
+ @root = root
119
+ @glob = options[:glob]
120
+ @exclude = options[:exclude]
120
121
 
121
122
  options[:autoload_once] ? autoload_once! : skip_autoload_once!
122
123
  options[:eager_load] ? eager_load! : skip_eager_load!
@@ -189,13 +190,11 @@ module Rails
189
190
  raise "You need to set a path root" unless @root.path
190
191
  result = []
191
192
 
192
- each do |p|
193
- path = File.expand_path(p, @root.path)
193
+ each do |path|
194
+ path = File.expand_path(path, @root.path)
194
195
 
195
196
  if @glob && File.directory?(path)
196
- Dir.chdir(path) do
197
- result.concat(Dir.glob(@glob).map { |file| File.join path, file }.sort)
198
- end
197
+ result.concat files_in(path)
199
198
  else
200
199
  result << path
201
200
  end
@@ -222,6 +221,17 @@ module Rails
222
221
  end
223
222
 
224
223
  alias to_a expanded
224
+
225
+ private
226
+
227
+ def files_in(path)
228
+ Dir.chdir(path) do
229
+ files = Dir.glob(@glob)
230
+ files -= @exclude if @exclude
231
+ files.map! { |file| File.join(path, file) }
232
+ files.sort
233
+ end
234
+ end
225
235
  end
226
236
  end
227
237
  end
@@ -224,7 +224,7 @@ module Rails
224
224
  end
225
225
 
226
226
  def railtie_namespace #:nodoc:
227
- @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
227
+ @railtie_namespace ||= self.class.module_parents.detect { |n| n.respond_to?(:railtie_namespace) }
228
228
  end
229
229
 
230
230
  protected
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.2.2") && RUBY_ENGINE == "ruby"
3
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5.0") && RUBY_ENGINE == "ruby"
4
4
  desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
5
5
  abort <<-end_message
6
6
 
7
- Rails 5 requires Ruby 2.2.2 or newer.
7
+ Rails 6 requires Ruby 2.5.0 or newer.
8
8
 
9
9
  You're running
10
10
  #{desc}
11
11
 
12
- Please upgrade to Ruby 2.2.2 or newer to continue.
12
+ Please upgrade to Ruby 2.5.0 or newer to continue.
13
13
 
14
14
  end_message
15
15
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "yaml"
4
4
  require "active_support/message_encryptor"
5
- require "active_support/core_ext/string/strip"
6
5
 
7
6
  module Rails
8
7
  # Greatly inspired by Ara T. Howard's magnificent sekrets gem. 😘
@@ -1,141 +1,149 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Implements the logic behind the rake tasks for annotations like
4
- #
5
- # rails notes
6
- # rails notes:optimize
7
- #
8
- # and friends. See <tt>rails -T notes</tt> and <tt>railties/lib/rails/tasks/annotations.rake</tt>.
9
- #
10
- # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
11
- # represent the line where the annotation lives, its tag, and its text. Note
12
- # the filename is not stored.
13
- #
14
- # Annotations are looked for in comments and modulus whitespace they have to
15
- # start with the tag optionally followed by a colon. Everything up to the end
16
- # of the line (or closing ERB comment tag) is considered to be their text.
17
- class SourceAnnotationExtractor
18
- Annotation = Struct.new(:line, :tag, :text) do
19
- def self.directories
20
- @@directories ||= %w(app config db lib test) + (ENV["SOURCE_ANNOTATION_DIRECTORIES"] || "").split(",")
21
- end
3
+ require "active_support/deprecation"
22
4
 
23
- # Registers additional directories to be included
24
- # SourceAnnotationExtractor::Annotation.register_directories("spec", "another")
25
- def self.register_directories(*dirs)
26
- directories.push(*dirs)
27
- end
5
+ # Remove this deprecated class in the next minor version
6
+ #:nodoc:
7
+ SourceAnnotationExtractor = ActiveSupport::Deprecation::DeprecatedConstantProxy.
8
+ new("SourceAnnotationExtractor", "Rails::SourceAnnotationExtractor")
28
9
 
29
- def self.extensions
30
- @@extensions ||= {}
31
- end
10
+ module Rails
11
+ # Implements the logic behind <tt>Rails::Command::NotesCommand</tt>. See <tt>rails notes --help</tt> for usage information.
12
+ #
13
+ # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
14
+ # represent the line where the annotation lives, its tag, and its text. Note
15
+ # the filename is not stored.
16
+ #
17
+ # Annotations are looked for in comments and modulus whitespace they have to
18
+ # start with the tag optionally followed by a colon. Everything up to the end
19
+ # of the line (or closing ERB comment tag) is considered to be their text.
20
+ class SourceAnnotationExtractor
21
+ class Annotation < Struct.new(:line, :tag, :text)
22
+ def self.directories
23
+ @@directories ||= %w(app config db lib test)
24
+ end
32
25
 
33
- # Registers new Annotations File Extensions
34
- # SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
35
- def self.register_extensions(*exts, &block)
36
- extensions[/\.(#{exts.join("|")})$/] = block
37
- end
26
+ # Registers additional directories to be included
27
+ # Rails::SourceAnnotationExtractor::Annotation.register_directories("spec", "another")
28
+ def self.register_directories(*dirs)
29
+ directories.push(*dirs)
30
+ end
31
+
32
+ def self.extensions
33
+ @@extensions ||= {}
34
+ end
38
35
 
39
- register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ }
40
- register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
41
- register_extensions("erb") { |tag| /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ }
36
+ # Registers new Annotations File Extensions
37
+ # Rails::SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
38
+ def self.register_extensions(*exts, &block)
39
+ extensions[/\.(#{exts.join("|")})$/] = block
40
+ end
41
+
42
+ register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ }
43
+ register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
44
+ register_extensions("erb") { |tag| /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ }
45
+
46
+ # Returns a representation of the annotation that looks like this:
47
+ #
48
+ # [126] [TODO] This algorithm is simple and clearly correct, make it faster.
49
+ #
50
+ # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
51
+ # Otherwise the string contains just line and text.
52
+ def to_s(options = {})
53
+ s = +"[#{line.to_s.rjust(options[:indent])}] "
54
+ s << "[#{tag}] " if options[:tag]
55
+ s << text
56
+ end
57
+
58
+ # Used in annotations.rake
59
+ #:nodoc:
60
+ def self.notes_task_deprecation_warning
61
+ ActiveSupport::Deprecation.warn("This rake task is deprecated and will be removed in Rails 6.1. \nRefer to `rails notes --help` for more information.\n")
62
+ puts "\n"
63
+ end
64
+ end
42
65
 
43
- # Returns a representation of the annotation that looks like this:
66
+ # Prints all annotations with tag +tag+ under the root directories +app+,
67
+ # +config+, +db+, +lib+, and +test+ (recursively).
68
+ #
69
+ # Specific directories can be explicitly set using the <tt>:dirs</tt> key in +options+.
70
+ #
71
+ # Rails::SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
44
72
  #
45
- # [126] [TODO] This algorithm is simple and clearly correct, make it faster.
73
+ # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+.
46
74
  #
47
- # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
48
- # Otherwise the string contains just line and text.
49
- def to_s(options = {})
50
- s = "[#{line.to_s.rjust(options[:indent])}] ".dup
51
- s << "[#{tag}] " if options[:tag]
52
- s << text
75
+ # See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
76
+ #
77
+ # This class method is the single entry point for the `rails notes` command.
78
+ def self.enumerate(tag, options = {})
79
+ extractor = new(tag)
80
+ dirs = options.delete(:dirs) || Annotation.directories
81
+ extractor.display(extractor.find(dirs), options)
53
82
  end
54
- end
55
83
 
56
- # Prints all annotations with tag +tag+ under the root directories +app+,
57
- # +config+, +db+, +lib+, and +test+ (recursively).
58
- #
59
- # Additional directories may be added using a comma-delimited list set using
60
- # <tt>ENV['SOURCE_ANNOTATION_DIRECTORIES']</tt>.
61
- #
62
- # Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+.
63
- #
64
- # SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
65
- #
66
- # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+.
67
- #
68
- # See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
69
- #
70
- # This class method is the single entry point for the rake tasks.
71
- def self.enumerate(tag, options = {})
72
- extractor = new(tag)
73
- dirs = options.delete(:dirs) || Annotation.directories
74
- extractor.display(extractor.find(dirs), options)
75
- end
76
-
77
- attr_reader :tag
78
-
79
- def initialize(tag)
80
- @tag = tag
81
- end
84
+ attr_reader :tag
82
85
 
83
- # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
84
- # with their annotations.
85
- def find(dirs)
86
- dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
87
- end
86
+ def initialize(tag)
87
+ @tag = tag
88
+ end
88
89
 
89
- # Returns a hash that maps filenames under +dir+ (recursively) to arrays
90
- # with their annotations. Only files with annotations are included. Files
91
- # with extension +.builder+, +.rb+, +.rake+, +.yml+, +.yaml+, +.ruby+,
92
- # +.css+, +.js+ and +.erb+ are taken into account.
93
- def find_in(dir)
94
- results = {}
95
-
96
- Dir.glob("#{dir}/*") do |item|
97
- next if File.basename(item)[0] == ?.
98
-
99
- if File.directory?(item)
100
- results.update(find_in(item))
101
- else
102
- extension = Annotation.extensions.detect do |regexp, _block|
103
- regexp.match(item)
104
- end
90
+ # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
91
+ # with their annotations.
92
+ def find(dirs)
93
+ dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
94
+ end
105
95
 
106
- if extension
107
- pattern = extension.last.call(tag)
108
- results.update(extract_annotations_from(item, pattern)) if pattern
96
+ # Returns a hash that maps filenames under +dir+ (recursively) to arrays
97
+ # with their annotations. Files with extensions registered in
98
+ # <tt>Rails::SourceAnnotationExtractor::Annotation.extensions</tt> are
99
+ # taken into account. Only files with annotations are included.
100
+ def find_in(dir)
101
+ results = {}
102
+
103
+ Dir.glob("#{dir}/*") do |item|
104
+ next if File.basename(item)[0] == ?.
105
+
106
+ if File.directory?(item)
107
+ results.update(find_in(item))
108
+ else
109
+ extension = Annotation.extensions.detect do |regexp, _block|
110
+ regexp.match(item)
111
+ end
112
+
113
+ if extension
114
+ pattern = extension.last.call(tag)
115
+ results.update(extract_annotations_from(item, pattern)) if pattern
116
+ end
109
117
  end
110
118
  end
111
- end
112
119
 
113
- results
114
- end
120
+ results
121
+ end
115
122
 
116
- # If +file+ is the filename of a file that contains annotations this method returns
117
- # a hash with a single entry that maps +file+ to an array of its annotations.
118
- # Otherwise it returns an empty hash.
119
- def extract_annotations_from(file, pattern)
120
- lineno = 0
121
- result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line|
122
- lineno += 1
123
- next list unless line =~ pattern
124
- list << Annotation.new(lineno, $1, $2)
123
+ # If +file+ is the filename of a file that contains annotations this method returns
124
+ # a hash with a single entry that maps +file+ to an array of its annotations.
125
+ # Otherwise it returns an empty hash.
126
+ def extract_annotations_from(file, pattern)
127
+ lineno = 0
128
+ result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line|
129
+ lineno += 1
130
+ next list unless line =~ pattern
131
+ list << Annotation.new(lineno, $1, $2)
132
+ end
133
+ result.empty? ? {} : { file => result }
125
134
  end
126
- result.empty? ? {} : { file => result }
127
- end
128
135
 
129
- # Prints the mapping from filenames to annotations in +results+ ordered by filename.
130
- # The +options+ hash is passed to each annotation's +to_s+.
131
- def display(results, options = {})
132
- options[:indent] = results.flat_map { |f, a| a.map(&:line) }.max.to_s.size
133
- results.keys.sort.each do |file|
134
- puts "#{file}:"
135
- results[file].each do |note|
136
- puts " * #{note.to_s(options)}"
136
+ # Prints the mapping from filenames to annotations in +results+ ordered by filename.
137
+ # The +options+ hash is passed to each annotation's +to_s+.
138
+ def display(results, options = {})
139
+ options[:indent] = results.flat_map { |f, a| a.map(&:line) }.max.to_s.size
140
+ results.keys.sort.each do |file|
141
+ puts "#{file}:"
142
+ results[file].each do |note|
143
+ puts " * #{note.to_s(options)}"
144
+ end
145
+ puts
137
146
  end
138
- puts
139
147
  end
140
148
  end
141
149
  end