loading
Generated 2020-08-24T22:49:24-04:00

All Files ( 15.5% covered at 2.4 hits/line )

138 files in total.
7213 relevant lines, 1118 lines covered and 6095 lines missed. ( 15.5% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
lib/minitest/rails_plugin.rb 0.00 % 59 39 0 39 0.00
lib/rails.rb 0.00 % 116 66 0 66 0.00
lib/rails/all.rb 100.00 % 24 4 4 0 102.00
lib/rails/api/generator.rb 0.00 % 38 27 0 27 0.00
lib/rails/api/task.rb 0.00 % 202 160 0 160 0.00
lib/rails/app_loader.rb 0.00 % 77 53 0 53 0.00
lib/rails/app_updater.rb 0.00 % 37 32 0 32 0.00
lib/rails/application.rb 0.00 % 632 329 0 329 0.00
lib/rails/application/bootstrap.rb 0.00 % 81 63 0 63 0.00
lib/rails/application/configuration.rb 46.72 % 403 229 107 122 8.09
lib/rails/application/default_middleware_stack.rb 0.00 % 111 85 0 85 0.00
lib/rails/application/dummy_erb_compiler.rb 0.00 % 18 12 0 12 0.00
lib/rails/application/finisher.rb 0.00 % 263 177 0 177 0.00
lib/rails/application/routes_reloader.rb 0.00 % 61 50 0 50 0.00
lib/rails/application_controller.rb 0.00 % 28 21 0 21 0.00
lib/rails/autoloaders.rb 0.00 % 48 39 0 39 0.00
lib/rails/backtrace_cleaner.rb 0.00 % 27 23 0 23 0.00
lib/rails/cli.rb 0.00 % 19 11 0 11 0.00
lib/rails/code_statistics.rb 0.00 % 117 94 0 94 0.00
lib/rails/code_statistics_calculator.rb 0.00 % 88 75 0 75 0.00
lib/rails/command.rb 44.07 % 120 59 26 33 7.05
lib/rails/command/actions.rb 0.00 % 54 42 0 42 0.00
lib/rails/command/base.rb 0.00 % 169 117 0 117 0.00
lib/rails/command/behavior.rb 36.59 % 84 41 15 26 6.02
lib/rails/command/environment_argument.rb 0.00 % 40 33 0 33 0.00
lib/rails/command/helpers/editor.rb 0.00 % 35 30 0 30 0.00
lib/rails/command/spellchecker.rb 0.00 % 57 42 0 42 0.00
lib/rails/commands.rb 0.00 % 18 13 0 13 0.00
lib/rails/commands/application/application_command.rb 0.00 % 31 23 0 23 0.00
lib/rails/commands/console/console_command.rb 0.00 % 106 78 0 78 0.00
lib/rails/commands/credentials/credentials_command.rb 0.00 % 137 106 0 106 0.00
lib/rails/commands/credentials/credentials_command/diffing.rb 0.00 % 41 33 0 33 0.00
lib/rails/commands/db/system/change/change_command.rb 0.00 % 25 20 0 20 0.00
lib/rails/commands/dbconsole/dbconsole_command.rb 0.00 % 195 147 0 147 0.00
lib/rails/commands/destroy/destroy_command.rb 0.00 % 28 21 0 21 0.00
lib/rails/commands/dev/dev_command.rb 0.00 % 19 15 0 15 0.00
lib/rails/commands/encrypted/encrypted_command.rb 0.00 % 87 67 0 67 0.00
lib/rails/commands/generate/generate_command.rb 0.00 % 30 22 0 22 0.00
lib/rails/commands/help/help_command.rb 0.00 % 15 11 0 11 0.00
lib/rails/commands/initializers/initializers_command.rb 0.00 % 23 17 0 17 0.00
lib/rails/commands/new/new_command.rb 0.00 % 19 16 0 16 0.00
lib/rails/commands/notes/notes_command.rb 0.00 % 39 29 0 29 0.00
lib/rails/commands/plugin/plugin_command.rb 0.00 % 45 34 0 34 0.00
lib/rails/commands/rake/rake_command.rb 0.00 % 53 41 0 41 0.00
lib/rails/commands/routes/routes_command.rb 0.00 % 37 29 0 29 0.00
lib/rails/commands/runner/runner_command.rb 0.00 % 57 45 0 45 0.00
lib/rails/commands/secrets/secrets_command.rb 0.00 % 65 53 0 53 0.00
lib/rails/commands/server/server_command.rb 0.00 % 314 254 0 254 0.00
lib/rails/commands/version/version_command.rb 0.00 % 11 9 0 9 0.00
lib/rails/configuration.rb 0.00 % 166 104 0 104 0.00
lib/rails/console/app.rb 0.00 % 38 25 0 25 0.00
lib/rails/console/helpers.rb 0.00 % 19 10 0 10 0.00
lib/rails/dev_caching.rb 0.00 % 44 34 0 34 0.00
lib/rails/engine.rb 0.00 % 734 294 0 294 0.00
lib/rails/engine/commands.rb 0.00 % 9 6 0 6 0.00
lib/rails/engine/configuration.rb 94.12 % 92 51 48 3 25.53
lib/rails/engine/railties.rb 0.00 % 23 18 0 18 0.00
lib/rails/engine/updater.rb 80.00 % 21 10 8 2 0.80
lib/rails/gem_version.rb 0.00 % 17 12 0 12 0.00
lib/rails/generators.rb 47.65 % 338 149 71 78 11.00
lib/rails/generators/actions.rb 22.07 % 370 145 32 113 3.53
lib/rails/generators/actions/create_migration.rb 38.64 % 75 44 17 27 0.39
lib/rails/generators/active_model.rb 54.55 % 80 22 12 10 1.64
lib/rails/generators/app_base.rb 43.93 % 460 214 94 120 1.45
lib/rails/generators/app_name.rb 53.85 % 50 26 14 12 1.62
lib/rails/generators/base.rb 63.82 % 424 152 97 55 41.93
lib/rails/generators/css/assets/assets_generator.rb 0.00 % 15 11 0 11 0.00
lib/rails/generators/css/scaffold/scaffold_generator.rb 0.00 % 18 11 0 11 0.00
lib/rails/generators/database.rb 31.25 % 57 32 10 22 0.94
lib/rails/generators/erb.rb 0.00 % 26 20 0 20 0.00
lib/rails/generators/erb/controller/controller_generator.rb 0.00 % 24 19 0 19 0.00
lib/rails/generators/erb/mailer/mailer_generator.rb 0.00 % 41 32 0 32 0.00
lib/rails/generators/erb/scaffold/scaffold_generator.rb 0.00 % 32 25 0 25 0.00
lib/rails/generators/generated_attribute.rb 34.23 % 202 111 38 73 4.45
lib/rails/generators/migration.rb 41.67 % 76 36 15 21 0.42
lib/rails/generators/model_helpers.rb 50.00 % 37 20 10 10 1.55
lib/rails/generators/named_base.rb 42.34 % 227 111 47 64 5.46
lib/rails/generators/rails/app/app_generator.rb 34.34 % 650 332 114 218 1.10
lib/rails/generators/rails/application_record/application_record_generator.rb 100.00 % 9 4 4 0 1.00
lib/rails/generators/rails/assets/assets_generator.rb 0.00 % 26 20 0 20 0.00
lib/rails/generators/rails/benchmark/benchmark_generator.rb 0.00 % 29 22 0 22 0.00
lib/rails/generators/rails/controller/controller_generator.rb 63.64 % 38 22 14 8 1.27
lib/rails/generators/rails/credentials/credentials_generator.rb 0.00 % 56 42 0 42 0.00
lib/rails/generators/rails/db/system/change/change_generator.rb 0.00 % 65 51 0 51 0.00
lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb 0.00 % 31 23 0 23 0.00
lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb 0.00 % 58 48 0 48 0.00
lib/rails/generators/rails/generator/generator_generator.rb 69.23 % 27 13 9 4 0.69
lib/rails/generators/rails/helper/helper_generator.rb 80.00 % 20 10 8 2 0.80
lib/rails/generators/rails/integration_test/integration_test_generator.rb 100.00 % 9 4 4 0 1.00
lib/rails/generators/rails/master_key/master_key_generator.rb 0.00 % 53 42 0 42 0.00
lib/rails/generators/rails/migration/migration_generator.rb 100.00 % 10 5 5 0 2.00
lib/rails/generators/rails/model/model_generator.rb 100.00 % 14 7 7 0 2.00
lib/rails/generators/rails/plugin/plugin_generator.rb 33.48 % 430 233 78 155 0.33
lib/rails/generators/rails/resource/resource_generator.rb 90.00 % 21 10 9 1 0.90
lib/rails/generators/rails/resource_route/resource_route_generator.rb 0.00 % 23 10 0 10 0.00
lib/rails/generators/rails/scaffold/scaffold_generator.rb 76.19 % 36 21 16 5 0.76
lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb 63.33 % 56 30 19 11 0.63
lib/rails/generators/rails/system_test/system_test_generator.rb 100.00 % 9 4 4 0 1.00
lib/rails/generators/rails/task/task_generator.rb 83.33 % 13 6 5 1 0.83
lib/rails/generators/resource_helpers.rb 43.90 % 82 41 18 23 0.88
lib/rails/generators/test_case.rb 100.00 % 37 13 13 0 16.00
lib/rails/generators/test_unit.rb 0.00 % 10 7 0 7 0.00
lib/rails/generators/test_unit/controller/controller_generator.rb 0.00 % 19 14 0 14 0.00
lib/rails/generators/test_unit/generator/generator_generator.rb 0.00 % 27 21 0 21 0.00
lib/rails/generators/test_unit/helper/helper_generator.rb 0.00 % 11 7 0 7 0.00
lib/rails/generators/test_unit/integration/integration_generator.rb 0.00 % 20 15 0 15 0.00
lib/rails/generators/test_unit/job/job_generator.rb 0.00 % 20 15 0 15 0.00
lib/rails/generators/test_unit/mailer/mailer_generator.rb 0.00 % 28 21 0 21 0.00
lib/rails/generators/test_unit/model/model_generator.rb 0.00 % 37 28 0 28 0.00
lib/rails/generators/test_unit/plugin/plugin_generator.rb 0.00 % 15 11 0 11 0.00
lib/rails/generators/test_unit/plugin/templates/test_helper.rb 0.00 % 2 2 0 2 0.00
lib/rails/generators/test_unit/scaffold/scaffold_generator.rb 0.00 % 68 53 0 53 0.00
lib/rails/generators/test_unit/system/system_generator.rb 0.00 % 24 18 0 18 0.00
lib/rails/generators/testing/assertions.rb 40.54 % 127 37 15 22 6.49
lib/rails/generators/testing/behaviour.rb 70.21 % 113 47 33 14 11.38
lib/rails/generators/testing/setup_and_teardown.rb 54.55 % 20 11 6 5 8.73
lib/rails/info.rb 0.00 % 105 77 0 77 0.00
lib/rails/info_controller.rb 0.00 % 45 36 0 36 0.00
lib/rails/initializable.rb 0.00 % 95 75 0 75 0.00
lib/rails/mailers_controller.rb 0.00 % 101 81 0 81 0.00
lib/rails/paths.rb 0.00 % 243 156 0 156 0.00
lib/rails/plugin/test.rb 0.00 % 9 5 0 5 0.00
lib/rails/rack.rb 0.00 % 7 5 0 5 0.00
lib/rails/rack/logger.rb 0.00 % 79 63 0 63 0.00
lib/rails/railtie.rb 0.00 % 259 106 0 106 0.00
lib/rails/railtie/configurable.rb 0.00 % 36 27 0 27 0.00
lib/rails/railtie/configuration.rb 0.00 % 102 62 0 62 0.00
lib/rails/ruby_version_check.rb 0.00 % 15 8 0 8 0.00
lib/rails/secrets.rb 0.00 % 105 81 0 81 0.00
lib/rails/source_annotation_extractor.rb 39.68 % 162 63 25 38 7.83
lib/rails/tasks.rb 0.00 % 23 19 0 19 0.00
lib/rails/test_help.rb 0.00 % 50 36 0 36 0.00
lib/rails/test_unit/line_filtering.rb 66.67 % 13 6 4 2 11.33
lib/rails/test_unit/railtie.rb 71.43 % 29 14 10 4 12.14
lib/rails/test_unit/reporter.rb 0.00 % 111 91 0 91 0.00
lib/rails/test_unit/runner.rb 36.67 % 161 90 33 57 6.23
lib/rails/version.rb 0.00 % 10 6 0 6 0.00
lib/rails/welcome_controller.rb 0.00 % 10 6 0 6 0.00

lib/minitest/rails_plugin.rb

0.0% lines covered

39 relevant lines. 0 lines covered and 39 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/core_ext/module/attribute_accessors"
  3. require "rails/test_unit/reporter"
  4. require "rails/test_unit/runner"
  5. module Minitest
  6. class SuppressedSummaryReporter < SummaryReporter
  7. # Disable extra failure output after a run if output is inline.
  8. def aggregated_results(*)
  9. super unless options[:output_inline]
  10. end
  11. end
  12. def self.plugin_rails_options(opts, options)
  13. ::Rails::TestUnit::Runner.attach_before_load_options(opts)
  14. opts.on("-b", "--backtrace", "Show the complete backtrace") do
  15. options[:full_backtrace] = true
  16. end
  17. opts.on("-d", "--defer-output", "Output test failures and errors after the test run") do
  18. options[:output_inline] = false
  19. end
  20. opts.on("-f", "--fail-fast", "Abort test run on first failure or error") do
  21. options[:fail_fast] = true
  22. end
  23. opts.on("-c", "--[no-]color", "Enable color in the output") do |value|
  24. options[:color] = value
  25. end
  26. options[:color] = true
  27. options[:output_inline] = true
  28. end
  29. # Owes great inspiration to test runner trailblazers like RSpec,
  30. # minitest-reporters, maxitest and others.
  31. def self.plugin_rails_init(options)
  32. unless options[:full_backtrace] || ENV["BACKTRACE"]
  33. # Plugin can run without Rails loaded, check before filtering.
  34. Minitest.backtrace_filter = ::Rails.backtrace_cleaner if ::Rails.respond_to?(:backtrace_cleaner)
  35. end
  36. # Suppress summary reports when outputting inline rerun snippets.
  37. if reporter.reporters.reject! { |reporter| reporter.kind_of?(SummaryReporter) }
  38. reporter << SuppressedSummaryReporter.new(options[:io], options)
  39. end
  40. # Replace progress reporter for colors.
  41. if reporter.reporters.reject! { |reporter| reporter.kind_of?(ProgressReporter) }
  42. reporter << ::Rails::TestUnitReporter.new(options[:io], options)
  43. end
  44. end
  45. # Backwards compatibility with Rails 5.0 generated plugin test scripts
  46. mattr_reader :run_via, default: {}
  47. end

lib/rails.rb

0.0% lines covered

66 relevant lines. 0 lines covered and 66 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/ruby_version_check"
  3. require "pathname"
  4. require "active_support"
  5. require "active_support/core_ext/kernel/reporting"
  6. require "active_support/core_ext/module/delegation"
  7. require "active_support/core_ext/array/extract_options"
  8. require "active_support/core_ext/object/blank"
  9. require "rails/application"
  10. require "rails/version"
  11. require "rails/autoloaders"
  12. require "active_support/railtie"
  13. require "action_dispatch/railtie"
  14. # UTF-8 is the default internal and external encoding.
  15. silence_warnings do
  16. Encoding.default_external = Encoding::UTF_8
  17. Encoding.default_internal = Encoding::UTF_8
  18. end
  19. module Rails
  20. extend ActiveSupport::Autoload
  21. autoload :Info
  22. autoload :InfoController
  23. autoload :MailersController
  24. autoload :WelcomeController
  25. class << self
  26. @application = @app_class = nil
  27. attr_writer :application
  28. attr_accessor :app_class, :cache, :logger
  29. def application
  30. @application ||= (app_class.instance if app_class)
  31. end
  32. delegate :initialize!, :initialized?, to: :application
  33. # The Configuration instance used to configure the Rails environment
  34. def configuration
  35. application.config
  36. end
  37. def backtrace_cleaner
  38. @backtrace_cleaner ||= begin
  39. # Relies on Active Support, so we have to lazy load to postpone definition until Active Support has been loaded
  40. require "rails/backtrace_cleaner"
  41. Rails::BacktraceCleaner.new
  42. end
  43. end
  44. # Returns a Pathname object of the current Rails project,
  45. # otherwise it returns +nil+ if there is no project:
  46. #
  47. # Rails.root
  48. # # => #<Pathname:/Users/someuser/some/path/project>
  49. def root
  50. application && application.config.root
  51. end
  52. # Returns the current Rails environment.
  53. #
  54. # Rails.env # => "development"
  55. # Rails.env.development? # => true
  56. # Rails.env.production? # => false
  57. def env
  58. @_env ||= ActiveSupport::EnvironmentInquirer.new(ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development")
  59. end
  60. # Sets the Rails environment.
  61. #
  62. # Rails.env = "staging" # => "staging"
  63. def env=(environment)
  64. @_env = ActiveSupport::EnvironmentInquirer.new(environment)
  65. end
  66. # Returns all Rails groups for loading based on:
  67. #
  68. # * The Rails environment;
  69. # * The environment variable RAILS_GROUPS;
  70. # * The optional envs given as argument and the hash with group dependencies;
  71. #
  72. # Rails.groups assets: [:development, :test]
  73. # # => [:default, "development", :assets] for Rails.env == "development"
  74. # # => [:default, "production"] for Rails.env == "production"
  75. def groups(*groups)
  76. hash = groups.extract_options!
  77. env = Rails.env
  78. groups.unshift(:default, env)
  79. groups.concat ENV["RAILS_GROUPS"].to_s.split(",")
  80. groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
  81. groups.compact!
  82. groups.uniq!
  83. groups
  84. end
  85. # Returns a Pathname object of the public folder of the current
  86. # Rails project, otherwise it returns +nil+ if there is no project:
  87. #
  88. # Rails.public_path
  89. # # => #<Pathname:/Users/someuser/some/path/project/public>
  90. def public_path
  91. application && Pathname.new(application.paths["public"].first)
  92. end
  93. def autoloaders
  94. Autoloaders
  95. end
  96. end
  97. end

lib/rails/all.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. # rubocop:disable Style/RedundantBegin
  3. 17 require "rails"
  4. %w(
  5. active_record/railtie
  6. active_storage/engine
  7. action_controller/railtie
  8. action_view/railtie
  9. action_mailer/railtie
  10. active_job/railtie
  11. action_cable/engine
  12. action_mailbox/engine
  13. action_text/engine
  14. rails/test_unit/railtie
  15. sprockets/railtie
  16. 17 ).each do |railtie|
  17. 187 begin
  18. 187 require railtie
  19. rescue LoadError
  20. end
  21. end

lib/rails/api/generator.rb

0.0% lines covered

27 relevant lines. 0 lines covered and 27 lines missed.
    
  1. # frozen_string_literal: true
  2. require "sdoc"
  3. require "active_support/core_ext/array/extract"
  4. class RDoc::Generator::API < RDoc::Generator::SDoc # :nodoc:
  5. RDoc::RDoc.add_generator self
  6. def generate_class_tree_level(classes, visited = {})
  7. # Only process core extensions on the first visit and remove
  8. # Active Storage duplicated classes that are at the top level
  9. # since they aren't nested under a definition of the `ActiveStorage` module.
  10. if visited.empty?
  11. classes = classes.reject { |klass| active_storage?(klass) }
  12. core_exts = classes.extract! { |klass| core_extension?(klass) }
  13. super.unshift([ "Core extensions", "", "", build_core_ext_subtree(core_exts, visited) ])
  14. else
  15. super
  16. end
  17. end
  18. private
  19. def build_core_ext_subtree(classes, visited)
  20. classes.map do |klass|
  21. [ klass.name, klass.document_self_or_methods ? klass.path : "", "",
  22. generate_class_tree_level(klass.classes_and_modules, visited) ]
  23. end
  24. end
  25. def core_extension?(klass)
  26. klass.name != "ActiveSupport" && klass.in_files.any? { |file| file.absolute_name.include?("core_ext") }
  27. end
  28. def active_storage?(klass)
  29. klass.name != "ActiveStorage" && klass.in_files.all? { |file| file.absolute_name.include?("active_storage") }
  30. end
  31. end

lib/rails/api/task.rb

0.0% lines covered

160 relevant lines. 0 lines covered and 160 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rdoc/task"
  3. require "rails/api/generator"
  4. module Rails
  5. module API
  6. class Task < RDoc::Task
  7. RDOC_FILES = {
  8. "activesupport" => {
  9. include: %w(
  10. README.rdoc
  11. lib/active_support/**/*.rb
  12. )
  13. },
  14. "activerecord" => {
  15. include: %w(
  16. README.rdoc
  17. lib/active_record/**/*.rb
  18. lib/arel.rb
  19. )
  20. },
  21. "activemodel" => {
  22. include: %w(
  23. README.rdoc
  24. lib/active_model/**/*.rb
  25. )
  26. },
  27. "actionpack" => {
  28. include: %w(
  29. README.rdoc
  30. lib/abstract_controller/**/*.rb
  31. lib/action_controller/**/*.rb
  32. lib/action_dispatch/**/*.rb
  33. )
  34. },
  35. "actionview" => {
  36. include: %w(
  37. README.rdoc
  38. lib/action_view/**/*.rb
  39. ),
  40. exclude: "lib/action_view/vendor/*"
  41. },
  42. "actionmailer" => {
  43. include: %w(
  44. README.rdoc
  45. lib/action_mailer/**/*.rb
  46. )
  47. },
  48. "activejob" => {
  49. include: %w(
  50. README.md
  51. lib/active_job/**/*.rb
  52. )
  53. },
  54. "actioncable" => {
  55. include: %w(
  56. README.md
  57. lib/action_cable/**/*.rb
  58. )
  59. },
  60. "activestorage" => {
  61. include: %w(
  62. README.md
  63. app/**/active_storage/**/*.rb
  64. lib/active_storage/**/*.rb
  65. )
  66. },
  67. "actionmailbox" => {
  68. include: %w(
  69. README.md
  70. app/**/action_mailbox/**/*.rb
  71. lib/action_mailbox/**/*.rb
  72. )
  73. },
  74. "actiontext" => {
  75. include: %w(
  76. README.md
  77. app/**/action_text/**/*.rb
  78. lib/action_text/**/*.rb
  79. )
  80. },
  81. "railties" => {
  82. include: %w(
  83. README.rdoc
  84. lib/**/*.rb
  85. ),
  86. exclude: %w(
  87. lib/rails/generators/**/templates/**/*.rb
  88. lib/rails/test_unit/*
  89. lib/rails/api/generator.rb
  90. )
  91. }
  92. }
  93. def initialize(name)
  94. super
  95. # Every time rake runs this task is instantiated as all the rest.
  96. # Be lazy computing stuff to have as light impact as possible to
  97. # the rest of tasks.
  98. before_running_rdoc do
  99. configure_sdoc
  100. configure_rdoc_files
  101. setup_horo_variables
  102. end
  103. end
  104. # Hack, ignore the desc calls performed by the original initializer.
  105. def desc(description)
  106. # no-op
  107. end
  108. def configure_sdoc
  109. self.title = "Ruby on Rails API"
  110. self.rdoc_dir = api_dir
  111. options << "-m" << api_main
  112. options << "-e" << "UTF-8"
  113. options << "-f" << "api"
  114. options << "-T" << "rails"
  115. end
  116. def configure_rdoc_files
  117. rdoc_files.include(api_main)
  118. RDOC_FILES.each do |component, cfg|
  119. cdr = component_root_dir(component)
  120. Array(cfg[:include]).each do |pattern|
  121. rdoc_files.include("#{cdr}/#{pattern}")
  122. end
  123. Array(cfg[:exclude]).each do |pattern|
  124. rdoc_files.exclude("#{cdr}/#{pattern}")
  125. end
  126. end
  127. # Only generate documentation for files that have been
  128. # changed since the API was generated.
  129. if Dir.exist?("doc/rdoc") && !ENV["ALL"]
  130. last_generation = DateTime.rfc2822(File.open("doc/rdoc/created.rid", &:readline))
  131. rdoc_files.keep_if do |file|
  132. File.mtime(file).to_datetime > last_generation
  133. end
  134. # Nothing to do
  135. exit(0) if rdoc_files.empty?
  136. end
  137. end
  138. def setup_horo_variables
  139. ENV["HORO_PROJECT_NAME"] = "Ruby on Rails"
  140. ENV["HORO_PROJECT_VERSION"] = rails_version
  141. end
  142. def api_main
  143. component_root_dir("railties") + "/RDOC_MAIN.rdoc"
  144. end
  145. end
  146. class RepoTask < Task
  147. def configure_sdoc
  148. super
  149. options << "-g" # link to GitHub, SDoc flag
  150. end
  151. def component_root_dir(component)
  152. component
  153. end
  154. def api_dir
  155. "doc/rdoc"
  156. end
  157. end
  158. class EdgeTask < RepoTask
  159. def rails_version
  160. "master@#{`git rev-parse HEAD`[0, 7]}"
  161. end
  162. end
  163. class StableTask < RepoTask
  164. def rails_version
  165. File.read("RAILS_VERSION").strip
  166. end
  167. end
  168. end
  169. end

lib/rails/app_loader.rb

0.0% lines covered

53 relevant lines. 0 lines covered and 53 lines missed.
    
  1. # frozen_string_literal: true
  2. require "pathname"
  3. require "rails/version"
  4. module Rails
  5. module AppLoader # :nodoc:
  6. extend self
  7. RUBY = Gem.ruby
  8. EXECUTABLES = ["bin/rails", "script/rails"]
  9. BUNDLER_WARNING = <<EOS
  10. Beginning in Rails 4, Rails ships with a `rails` binstub at ./bin/rails that
  11. should be used instead of the Bundler-generated `rails` binstub.
  12. If you are seeing this message, your binstub at ./bin/rails was generated by
  13. Bundler instead of Rails.
  14. You might need to regenerate your `rails` binstub locally and add it to source
  15. control:
  16. rails app:update:bin # Bear in mind this generates other binstubs
  17. # too that you may or may not want (like yarn)
  18. If you already have Rails binstubs in source control, you might be
  19. inadvertently overwriting them during deployment by using bundle install
  20. with the --binstubs option.
  21. If your application was created prior to Rails 4, here's how to upgrade:
  22. bundle config --delete bin # Turn off Bundler's stub generator
  23. rails app:update:bin # Use the new Rails executables
  24. git add bin # Add bin/ to source control
  25. You may need to remove bin/ from your .gitignore as well.
  26. When you install a gem whose executable you want to use in your app,
  27. generate it and add it to source control:
  28. bundle binstubs some-gem-name
  29. git add bin/new-executable
  30. EOS
  31. def exec_app
  32. original_cwd = Dir.pwd
  33. loop do
  34. if exe = find_executable
  35. contents = File.read(exe)
  36. if /(APP|ENGINE)_PATH/.match?(contents)
  37. exec RUBY, exe, *ARGV
  38. break # non reachable, hack to be able to stub exec in the test suite
  39. elsif exe.end_with?("bin/rails") && contents.include?("This file was generated by Bundler")
  40. $stderr.puts(BUNDLER_WARNING)
  41. Object.const_set(:APP_PATH, File.expand_path("config/application", Dir.pwd))
  42. require File.expand_path("../boot", APP_PATH)
  43. require "rails/commands"
  44. break
  45. end
  46. end
  47. # If we exhaust the search there is no executable, this could be a
  48. # call to generate a new application, so restore the original cwd.
  49. Dir.chdir(original_cwd) && return if Pathname.new(Dir.pwd).root?
  50. # Otherwise keep moving upwards in search of an executable.
  51. Dir.chdir("..")
  52. end
  53. end
  54. def find_executable
  55. EXECUTABLES.find { |exe| File.file?(exe) }
  56. end
  57. end
  58. end

lib/rails/app_updater.rb

0.0% lines covered

32 relevant lines. 0 lines covered and 32 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators"
  3. require "rails/generators/rails/app/app_generator"
  4. module Rails
  5. class AppUpdater # :nodoc:
  6. class << self
  7. def invoke_from_app_generator(method)
  8. app_generator.send(method)
  9. end
  10. def app_generator
  11. @app_generator ||= begin
  12. gen = Rails::Generators::AppGenerator.new ["rails"], generator_options, destination_root: Rails.root
  13. File.exist?(Rails.root.join("config", "application.rb")) ? gen.send(:app_const) : gen.send(:valid_const?)
  14. gen
  15. end
  16. end
  17. private
  18. def generator_options
  19. options = { api: !!Rails.application.config.api_only, update: true }
  20. options[:skip_javascript] = !File.exist?(Rails.root.join("bin", "yarn"))
  21. options[:skip_active_record] = !defined?(ActiveRecord::Railtie)
  22. options[:skip_active_storage] = !defined?(ActiveStorage::Engine) || !defined?(ActiveRecord::Railtie)
  23. options[:skip_action_mailer] = !defined?(ActionMailer::Railtie)
  24. options[:skip_action_cable] = !defined?(ActionCable::Engine)
  25. options[:skip_sprockets] = !defined?(Sprockets::Railtie)
  26. options[:skip_puma] = !defined?(Puma)
  27. options[:skip_bootsnap] = !defined?(Bootsnap)
  28. options[:skip_spring] = !defined?(Spring)
  29. options
  30. end
  31. end
  32. end
  33. end

lib/rails/application.rb

0.0% lines covered

329 relevant lines. 0 lines covered and 329 lines missed.
    
  1. # frozen_string_literal: true
  2. require "yaml"
  3. require "active_support/core_ext/hash/keys"
  4. require "active_support/core_ext/object/blank"
  5. require "active_support/key_generator"
  6. require "active_support/message_verifier"
  7. require "active_support/encrypted_configuration"
  8. require "active_support/deprecation"
  9. require "active_support/hash_with_indifferent_access"
  10. require "active_support/configuration_file"
  11. require "rails/engine"
  12. require "rails/secrets"
  13. module Rails
  14. # An Engine with the responsibility of coordinating the whole boot process.
  15. #
  16. # == Initialization
  17. #
  18. # Rails::Application is responsible for executing all railties and engines
  19. # initializers. It also executes some bootstrap initializers (check
  20. # Rails::Application::Bootstrap) and finishing initializers, after all the others
  21. # are executed (check Rails::Application::Finisher).
  22. #
  23. # == Configuration
  24. #
  25. # Besides providing the same configuration as Rails::Engine and Rails::Railtie,
  26. # the application object has several specific configurations, for example
  27. # "cache_classes", "consider_all_requests_local", "filter_parameters",
  28. # "logger" and so forth.
  29. #
  30. # Check Rails::Application::Configuration to see them all.
  31. #
  32. # == Routes
  33. #
  34. # The application object is also responsible for holding the routes and reloading routes
  35. # whenever the files change in development.
  36. #
  37. # == Middlewares
  38. #
  39. # The Application is also responsible for building the middleware stack.
  40. #
  41. # == Booting process
  42. #
  43. # The application is also responsible for setting up and executing the booting
  44. # process. From the moment you require "config/application.rb" in your app,
  45. # the booting process goes like this:
  46. #
  47. # 1) require "config/boot.rb" to set up load paths
  48. # 2) require railties and engines
  49. # 3) Define Rails.application as "class MyApp::Application < Rails::Application"
  50. # 4) Run config.before_configuration callbacks
  51. # 5) Load config/environments/ENV.rb
  52. # 6) Run config.before_initialize callbacks
  53. # 7) Run Railtie#initializer defined by railties, engines and application.
  54. # One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
  55. # 8) Custom Railtie#initializers added by railties, engines and applications are executed
  56. # 9) Build the middleware stack and run to_prepare callbacks
  57. # 10) Run config.before_eager_load and eager_load! if eager_load is true
  58. # 11) Run config.after_initialize callbacks
  59. #
  60. # == Multiple Applications
  61. #
  62. # If you decide to define multiple applications, then the first application
  63. # that is initialized will be set to +Rails.application+, unless you override
  64. # it with a different application.
  65. #
  66. # To create a new application, you can instantiate a new instance of a class
  67. # that has already been created:
  68. #
  69. # class Application < Rails::Application
  70. # end
  71. #
  72. # first_application = Application.new
  73. # second_application = Application.new(config: first_application.config)
  74. #
  75. # In the above example, the configuration from the first application was used
  76. # to initialize the second application. You can also use the +initialize_copy+
  77. # on one of the applications to create a copy of the application which shares
  78. # the configuration.
  79. #
  80. # If you decide to define Rake tasks, runners, or initializers in an
  81. # application other than +Rails.application+, then you must run them manually.
  82. class Application < Engine
  83. autoload :Bootstrap, "rails/application/bootstrap"
  84. autoload :Configuration, "rails/application/configuration"
  85. autoload :DefaultMiddlewareStack, "rails/application/default_middleware_stack"
  86. autoload :Finisher, "rails/application/finisher"
  87. autoload :Railties, "rails/engine/railties"
  88. autoload :RoutesReloader, "rails/application/routes_reloader"
  89. class << self
  90. def inherited(base)
  91. super
  92. Rails.app_class = base
  93. add_lib_to_load_path!(find_root(base.called_from))
  94. ActiveSupport.run_load_hooks(:before_configuration, base)
  95. end
  96. def instance
  97. super.run_load_hooks!
  98. end
  99. def create(initial_variable_values = {}, &block)
  100. new(initial_variable_values, &block).run_load_hooks!
  101. end
  102. def find_root(from)
  103. find_root_with_flag "config.ru", from, Dir.pwd
  104. end
  105. # Makes the +new+ method public.
  106. #
  107. # Note that Rails::Application inherits from Rails::Engine, which
  108. # inherits from Rails::Railtie and the +new+ method on Rails::Railtie is
  109. # private
  110. public :new
  111. end
  112. attr_accessor :assets, :sandbox
  113. alias_method :sandbox?, :sandbox
  114. attr_reader :reloaders, :reloader, :executor
  115. delegate :default_url_options, :default_url_options=, to: :routes
  116. INITIAL_VARIABLES = [:config, :railties, :routes_reloader, :reloaders,
  117. :routes, :helpers, :app_env_config, :secrets] # :nodoc:
  118. def initialize(initial_variable_values = {}, &block)
  119. super()
  120. @initialized = false
  121. @reloaders = []
  122. @routes_reloader = nil
  123. @app_env_config = nil
  124. @ordered_railties = nil
  125. @railties = nil
  126. @message_verifiers = {}
  127. @ran_load_hooks = false
  128. @executor = Class.new(ActiveSupport::Executor)
  129. @reloader = Class.new(ActiveSupport::Reloader)
  130. @reloader.executor = @executor
  131. # are these actually used?
  132. @initial_variable_values = initial_variable_values
  133. @block = block
  134. end
  135. # Returns true if the application is initialized.
  136. def initialized?
  137. @initialized
  138. end
  139. def run_load_hooks! # :nodoc:
  140. return self if @ran_load_hooks
  141. @ran_load_hooks = true
  142. @initial_variable_values.each do |variable_name, value|
  143. if INITIAL_VARIABLES.include?(variable_name)
  144. instance_variable_set("@#{variable_name}", value)
  145. end
  146. end
  147. instance_eval(&@block) if @block
  148. self
  149. end
  150. # Reload application routes regardless if they changed or not.
  151. def reload_routes!
  152. routes_reloader.reload!
  153. end
  154. # Returns the application's KeyGenerator
  155. def key_generator
  156. # number of iterations selected based on consultation with the google security
  157. # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
  158. @caching_key_generator ||= ActiveSupport::CachingKeyGenerator.new(
  159. ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
  160. )
  161. end
  162. # Returns a message verifier object.
  163. #
  164. # This verifier can be used to generate and verify signed messages in the application.
  165. #
  166. # It is recommended not to use the same verifier for different things, so you can get different
  167. # verifiers passing the +verifier_name+ argument.
  168. #
  169. # ==== Parameters
  170. #
  171. # * +verifier_name+ - the name of the message verifier.
  172. #
  173. # ==== Examples
  174. #
  175. # message = Rails.application.message_verifier('sensitive_data').generate('my sensible data')
  176. # Rails.application.message_verifier('sensitive_data').verify(message)
  177. # # => 'my sensible data'
  178. #
  179. # See the +ActiveSupport::MessageVerifier+ documentation for more information.
  180. def message_verifier(verifier_name)
  181. @message_verifiers[verifier_name] ||= begin
  182. secret = key_generator.generate_key(verifier_name.to_s)
  183. ActiveSupport::MessageVerifier.new(secret)
  184. end
  185. end
  186. # Convenience for loading config/foo.yml for the current Rails env.
  187. #
  188. # Examples:
  189. #
  190. # # config/exception_notification.yml:
  191. # production:
  192. # url: http://127.0.0.1:8080
  193. # namespace: my_app_production
  194. #
  195. # development:
  196. # url: http://localhost:3001
  197. # namespace: my_app_development
  198. #
  199. # # config/environments/production.rb
  200. # Rails.application.configure do
  201. # config.middleware.use ExceptionNotifier, config_for(:exception_notification)
  202. # end
  203. #
  204. # # You can also store configurations in a shared section which will be
  205. # # merged with the environment configuration
  206. #
  207. # # config/example.yml
  208. # shared:
  209. # foo:
  210. # bar:
  211. # baz: 1
  212. #
  213. # development:
  214. # foo:
  215. # bar:
  216. # qux: 2
  217. #
  218. # # development environment
  219. # Rails.application.config_for(:example)[:foo][:bar]
  220. # # => { baz: 1, qux: 2 }
  221. def config_for(name, env: Rails.env)
  222. yaml = name.is_a?(Pathname) ? name : Pathname.new("#{paths["config"].existent.first}/#{name}.yml")
  223. if yaml.exist?
  224. require "erb"
  225. all_configs = ActiveSupport::ConfigurationFile.parse(yaml, symbolize_names: true)
  226. config, shared = all_configs[env.to_sym], all_configs[:shared]
  227. if config.is_a?(Hash)
  228. ActiveSupport::OrderedOptions.new.update(shared&.deep_merge(config) || config)
  229. else
  230. config || shared
  231. end
  232. else
  233. raise "Could not load configuration. No such file - #{yaml}"
  234. end
  235. end
  236. # Stores some of the Rails initial environment parameters which
  237. # will be used by middlewares and engines to configure themselves.
  238. def env_config
  239. @app_env_config ||= begin
  240. super.merge(
  241. "action_dispatch.parameter_filter" => config.filter_parameters,
  242. "action_dispatch.redirect_filter" => config.filter_redirect,
  243. "action_dispatch.secret_key_base" => secret_key_base,
  244. "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
  245. "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
  246. "action_dispatch.logger" => Rails.logger,
  247. "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
  248. "action_dispatch.key_generator" => key_generator,
  249. "action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt,
  250. "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt,
  251. "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt,
  252. "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt,
  253. "action_dispatch.authenticated_encrypted_cookie_salt" => config.action_dispatch.authenticated_encrypted_cookie_salt,
  254. "action_dispatch.use_authenticated_cookie_encryption" => config.action_dispatch.use_authenticated_cookie_encryption,
  255. "action_dispatch.encrypted_cookie_cipher" => config.action_dispatch.encrypted_cookie_cipher,
  256. "action_dispatch.signed_cookie_digest" => config.action_dispatch.signed_cookie_digest,
  257. "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer,
  258. "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest,
  259. "action_dispatch.cookies_rotations" => config.action_dispatch.cookies_rotations,
  260. "action_dispatch.cookies_same_site_protection" => config.action_dispatch.cookies_same_site_protection,
  261. "action_dispatch.use_cookies_with_metadata" => config.action_dispatch.use_cookies_with_metadata,
  262. "action_dispatch.content_security_policy" => config.content_security_policy,
  263. "action_dispatch.content_security_policy_report_only" => config.content_security_policy_report_only,
  264. "action_dispatch.content_security_policy_nonce_generator" => config.content_security_policy_nonce_generator,
  265. "action_dispatch.content_security_policy_nonce_directives" => config.content_security_policy_nonce_directives,
  266. "action_dispatch.feature_policy" => config.feature_policy,
  267. )
  268. end
  269. end
  270. # If you try to define a set of Rake tasks on the instance, these will get
  271. # passed up to the Rake tasks defined on the application's class.
  272. def rake_tasks(&block)
  273. self.class.rake_tasks(&block)
  274. end
  275. # Sends the initializers to the +initializer+ method defined in the
  276. # Rails::Initializable module. Each Rails::Application class has its own
  277. # set of initializers, as defined by the Initializable module.
  278. def initializer(name, opts = {}, &block)
  279. self.class.initializer(name, opts, &block)
  280. end
  281. # Sends any runner called in the instance of a new application up
  282. # to the +runner+ method defined in Rails::Railtie.
  283. def runner(&blk)
  284. self.class.runner(&blk)
  285. end
  286. # Sends any console called in the instance of a new application up
  287. # to the +console+ method defined in Rails::Railtie.
  288. def console(&blk)
  289. self.class.console(&blk)
  290. end
  291. # Sends any generators called in the instance of a new application up
  292. # to the +generators+ method defined in Rails::Railtie.
  293. def generators(&blk)
  294. self.class.generators(&blk)
  295. end
  296. # Sends the +isolate_namespace+ method up to the class method.
  297. def isolate_namespace(mod)
  298. self.class.isolate_namespace(mod)
  299. end
  300. ## Rails internal API
  301. # This method is called just after an application inherits from Rails::Application,
  302. # allowing the developer to load classes in lib and use them during application
  303. # configuration.
  304. #
  305. # class MyApplication < Rails::Application
  306. # require "my_backend" # in lib/my_backend
  307. # config.i18n.backend = MyBackend
  308. # end
  309. #
  310. # Notice this method takes into consideration the default root path. So if you
  311. # are changing config.root inside your application definition or having a custom
  312. # Rails application, you will need to add lib to $LOAD_PATH on your own in case
  313. # you need to load files in lib/ during the application configuration as well.
  314. def self.add_lib_to_load_path!(root) #:nodoc:
  315. path = File.join root, "lib"
  316. if File.exist?(path) && !$LOAD_PATH.include?(path)
  317. $LOAD_PATH.unshift(path)
  318. end
  319. end
  320. def require_environment! #:nodoc:
  321. environment = paths["config/environment"].existent.first
  322. require environment if environment
  323. end
  324. def routes_reloader #:nodoc:
  325. @routes_reloader ||= RoutesReloader.new
  326. end
  327. # Returns an array of file paths appended with a hash of
  328. # directories-extensions suitable for ActiveSupport::FileUpdateChecker
  329. # API.
  330. def watchable_args #:nodoc:
  331. files, dirs = config.watchable_files.dup, config.watchable_dirs.dup
  332. ActiveSupport::Dependencies.autoload_paths.each do |path|
  333. File.file?(path) ? files << path.to_s : dirs[path.to_s] = [:rb]
  334. end
  335. [files, dirs]
  336. end
  337. # Initialize the application passing the given group. By default, the
  338. # group is :default
  339. def initialize!(group = :default) #:nodoc:
  340. raise "Application has been already initialized." if @initialized
  341. run_initializers(group, self)
  342. @initialized = true
  343. self
  344. end
  345. def initializers #:nodoc:
  346. Bootstrap.initializers_for(self) +
  347. railties_initializers(super) +
  348. Finisher.initializers_for(self)
  349. end
  350. def config #:nodoc:
  351. @config ||= Application::Configuration.new(self.class.find_root(self.class.called_from))
  352. end
  353. attr_writer :config
  354. # Returns secrets added to config/secrets.yml.
  355. #
  356. # Example:
  357. #
  358. # development:
  359. # secret_key_base: 836fa3665997a860728bcb9e9a1e704d427cfc920e79d847d79c8a9a907b9e965defa4154b2b86bdec6930adbe33f21364523a6f6ce363865724549fdfc08553
  360. # test:
  361. # secret_key_base: 5a37811464e7d378488b0f073e2193b093682e4e21f5d6f3ae0a4e1781e61a351fdc878a843424e81c73fb484a40d23f92c8dafac4870e74ede6e5e174423010
  362. # production:
  363. # secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  364. # namespace: my_app_production
  365. #
  366. # +Rails.application.secrets.namespace+ returns +my_app_production+ in the
  367. # production environment.
  368. def secrets
  369. @secrets ||= begin
  370. secrets = ActiveSupport::OrderedOptions.new
  371. files = config.paths["config/secrets"].existent
  372. files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets
  373. secrets.merge! Rails::Secrets.parse(files, env: Rails.env)
  374. # Fallback to config.secret_key_base if secrets.secret_key_base isn't set
  375. secrets.secret_key_base ||= config.secret_key_base
  376. secrets
  377. end
  378. end
  379. attr_writer :secrets
  380. # The secret_key_base is used as the input secret to the application's key generator, which in turn
  381. # is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
  382. #
  383. # In development and test, this is randomly generated and stored in a
  384. # temporary file in <tt>tmp/development_secret.txt</tt>.
  385. #
  386. # In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
  387. # then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
  388. # the correct place to store it is in the encrypted credentials file.
  389. def secret_key_base
  390. if Rails.env.development? || Rails.env.test?
  391. secrets.secret_key_base ||= generate_development_secret
  392. else
  393. validate_secret_key_base(
  394. ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
  395. )
  396. end
  397. end
  398. # Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with
  399. # the Rails master key, which is either taken from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading
  400. # +config/master.key+.
  401. # If specific credentials file exists for current environment, it takes precedence, thus for +production+
  402. # environment look first for +config/credentials/production.yml.enc+ with master key taken
  403. # from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading +config/credentials/production.key+.
  404. # Default behavior can be overwritten by setting +config.credentials.content_path+ and +config.credentials.key_path+.
  405. def credentials
  406. @credentials ||= encrypted(config.credentials.content_path, key_path: config.credentials.key_path)
  407. end
  408. # Shorthand to decrypt any encrypted configurations or files.
  409. #
  410. # For any file added with <tt>rails encrypted:edit</tt> call +read+ to decrypt
  411. # the file with the master key.
  412. # The master key is either stored in +config/master.key+ or <tt>ENV["RAILS_MASTER_KEY"]</tt>.
  413. #
  414. # Rails.application.encrypted("config/mystery_man.txt.enc").read
  415. # # => "We've met before, haven't we?"
  416. #
  417. # It's also possible to interpret encrypted YAML files with +config+.
  418. #
  419. # Rails.application.encrypted("config/credentials.yml.enc").config
  420. # # => { next_guys_line: "I don't think so. Where was it you think we met?" }
  421. #
  422. # Any top-level configs are also accessible directly on the return value:
  423. #
  424. # Rails.application.encrypted("config/credentials.yml.enc").next_guys_line
  425. # # => "I don't think so. Where was it you think we met?"
  426. #
  427. # The files or configs can also be encrypted with a custom key. To decrypt with
  428. # a key in the +ENV+, use:
  429. #
  430. # Rails.application.encrypted("config/special_tokens.yml.enc", env_key: "SPECIAL_TOKENS")
  431. #
  432. # Or to decrypt with a file, that should be version control ignored, relative to +Rails.root+:
  433. #
  434. # Rails.application.encrypted("config/special_tokens.yml.enc", key_path: "config/special_tokens.key")
  435. def encrypted(path, key_path: "config/master.key", env_key: "RAILS_MASTER_KEY")
  436. ActiveSupport::EncryptedConfiguration.new(
  437. config_path: Rails.root.join(path),
  438. key_path: Rails.root.join(key_path),
  439. env_key: env_key,
  440. raise_if_missing_key: config.require_master_key
  441. )
  442. end
  443. def to_app #:nodoc:
  444. self
  445. end
  446. def helpers_paths #:nodoc:
  447. config.helpers_paths
  448. end
  449. console do
  450. unless ::Kernel.private_method_defined?(:y)
  451. require "psych/y"
  452. end
  453. end
  454. # Return an array of railties respecting the order they're loaded
  455. # and the order specified by the +railties_order+ config.
  456. #
  457. # While running initializers we need engines in reverse order here when
  458. # copying migrations from railties ; we need them in the order given by
  459. # +railties_order+.
  460. def migration_railties # :nodoc:
  461. ordered_railties.flatten - [self]
  462. end
  463. # Eager loads the application code.
  464. def eager_load!
  465. if Rails.autoloaders.zeitwerk_enabled?
  466. Rails.autoloaders.each(&:eager_load)
  467. else
  468. super
  469. end
  470. end
  471. protected
  472. alias :build_middleware_stack :app
  473. def run_tasks_blocks(app) #:nodoc:
  474. railties.each { |r| r.run_tasks_blocks(app) }
  475. super
  476. require "rails/tasks"
  477. task :environment do
  478. ActiveSupport.on_load(:before_initialize) { config.eager_load = config.rake_eager_load }
  479. require_environment!
  480. end
  481. end
  482. def run_generators_blocks(app) #:nodoc:
  483. railties.each { |r| r.run_generators_blocks(app) }
  484. super
  485. end
  486. def run_runner_blocks(app) #:nodoc:
  487. railties.each { |r| r.run_runner_blocks(app) }
  488. super
  489. end
  490. def run_console_blocks(app) #:nodoc:
  491. railties.each { |r| r.run_console_blocks(app) }
  492. super
  493. end
  494. # Returns the ordered railties for this application considering railties_order.
  495. def ordered_railties #:nodoc:
  496. @ordered_railties ||= begin
  497. order = config.railties_order.map do |railtie|
  498. if railtie == :main_app
  499. self
  500. elsif railtie.respond_to?(:instance)
  501. railtie.instance
  502. else
  503. railtie
  504. end
  505. end
  506. all = (railties - order)
  507. all.push(self) unless (all + order).include?(self)
  508. order.push(:all) unless order.include?(:all)
  509. index = order.index(:all)
  510. order[index] = all
  511. order
  512. end
  513. end
  514. def railties_initializers(current) #:nodoc:
  515. initializers = []
  516. ordered_railties.reverse.flatten.each do |r|
  517. if r == self
  518. initializers += current
  519. else
  520. initializers += r.initializers
  521. end
  522. end
  523. initializers
  524. end
  525. def default_middleware_stack #:nodoc:
  526. default_stack = DefaultMiddlewareStack.new(self, config, paths)
  527. default_stack.build_stack
  528. end
  529. def validate_secret_key_base(secret_key_base)
  530. if secret_key_base.is_a?(String) && secret_key_base.present?
  531. secret_key_base
  532. elsif secret_key_base
  533. raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String`"
  534. else
  535. raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `bin/rails credentials:edit`"
  536. end
  537. end
  538. private
  539. def generate_development_secret
  540. if secrets.secret_key_base.nil?
  541. key_file = Rails.root.join("tmp/development_secret.txt")
  542. if !File.exist?(key_file)
  543. random_key = SecureRandom.hex(64)
  544. FileUtils.mkdir_p(key_file.dirname)
  545. File.binwrite(key_file, random_key)
  546. end
  547. secrets.secret_key_base = File.binread(key_file)
  548. end
  549. secrets.secret_key_base
  550. end
  551. def build_request(env)
  552. req = super
  553. env["ORIGINAL_FULLPATH"] = req.fullpath
  554. env["ORIGINAL_SCRIPT_NAME"] = req.script_name
  555. req
  556. end
  557. def build_middleware
  558. config.app_middleware + super
  559. end
  560. end
  561. end

lib/rails/application/bootstrap.rb

0.0% lines covered

63 relevant lines. 0 lines covered and 63 lines missed.
    
  1. # frozen_string_literal: true
  2. require "fileutils"
  3. require "active_support/notifications"
  4. require "active_support/dependencies"
  5. require "active_support/descendants_tracker"
  6. require "rails/secrets"
  7. module Rails
  8. class Application
  9. module Bootstrap
  10. include Initializable
  11. initializer :load_environment_hook, group: :all do end
  12. initializer :load_active_support, group: :all do
  13. require "active_support/all" unless config.active_support.bare
  14. end
  15. initializer :set_eager_load, group: :all do
  16. if config.eager_load.nil?
  17. warn <<~INFO
  18. config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
  19. * development - set it to false
  20. * test - set it to false (unless you use a tool that preloads your test environment)
  21. * production - set it to true
  22. INFO
  23. config.eager_load = config.cache_classes
  24. end
  25. end
  26. # Initialize the logger early in the stack in case we need to log some deprecation.
  27. initializer :initialize_logger, group: :all do
  28. Rails.logger ||= config.logger || begin
  29. logger = ActiveSupport::Logger.new(config.default_log_file)
  30. logger.formatter = config.log_formatter
  31. logger = ActiveSupport::TaggedLogging.new(logger)
  32. logger
  33. rescue StandardError
  34. path = config.paths["log"].first
  35. logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR))
  36. logger.level = ActiveSupport::Logger::WARN
  37. logger.warn(
  38. "Rails Error: Unable to access log file. Please ensure that #{path} exists and is writable " \
  39. "(ie, make it writable for user and group: chmod 0664 #{path}). " \
  40. "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
  41. )
  42. logger
  43. end
  44. Rails.logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase)
  45. end
  46. # Initialize cache early in the stack so railties can make use of it.
  47. initializer :initialize_cache, group: :all do
  48. unless Rails.cache
  49. Rails.cache = ActiveSupport::Cache.lookup_store(*config.cache_store)
  50. if Rails.cache.respond_to?(:middleware)
  51. config.middleware.insert_before(::Rack::Runtime, Rails.cache.middleware)
  52. end
  53. end
  54. end
  55. # Sets the dependency loading mechanism.
  56. initializer :initialize_dependency_mechanism, group: :all do
  57. ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
  58. end
  59. initializer :bootstrap_hook, group: :all do |app|
  60. ActiveSupport.run_load_hooks(:before_initialize, app)
  61. end
  62. initializer :set_secrets_root, group: :all do
  63. Rails::Secrets.root = root
  64. end
  65. end
  66. end
  67. end

lib/rails/application/configuration.rb

46.72% lines covered

229 relevant lines. 107 lines covered and 122 lines missed.
    
  1. # frozen_string_literal: true
  2. 17 require "ipaddr"
  3. 17 require "active_support/core_ext/kernel/reporting"
  4. 17 require "active_support/core_ext/symbol/starts_ends_with"
  5. 17 require "active_support/file_update_checker"
  6. 17 require "active_support/configuration_file"
  7. 17 require "rails/engine/configuration"
  8. 17 require "rails/source_annotation_extractor"
  9. 17 module Rails
  10. 17 class Application
  11. 17 class Configuration < ::Rails::Engine::Configuration
  12. 17 attr_accessor :allow_concurrency, :asset_host, :autoflush_log,
  13. :cache_classes, :cache_store, :consider_all_requests_local, :console,
  14. :eager_load, :exceptions_app, :file_watcher, :filter_parameters,
  15. :force_ssl, :helpers_paths, :hosts, :logger, :log_formatter, :log_tags,
  16. :railties_order, :relative_url_root, :secret_key_base,
  17. :ssl_options, :public_file_server,
  18. :session_options, :time_zone, :reload_classes_only_on_change,
  19. :beginning_of_week, :filter_redirect, :x, :enable_dependency_loading,
  20. :read_encrypted_secrets, :log_level, :content_security_policy_report_only,
  21. :content_security_policy_nonce_generator, :content_security_policy_nonce_directives,
  22. :require_master_key, :credentials, :disable_sandbox, :add_autoload_paths_to_load_path,
  23. :rake_eager_load
  24. 17 attr_reader :encoding, :api_only, :loaded_config_version, :autoloader
  25. 17 def initialize(*)
  26. 17 super
  27. 17 self.encoding = Encoding::UTF_8
  28. 17 @allow_concurrency = nil
  29. 17 @consider_all_requests_local = false
  30. 17 @filter_parameters = []
  31. 17 @filter_redirect = []
  32. 17 @helpers_paths = []
  33. 17 @hosts = Array(([".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")] if Rails.env.development?))
  34. 17 @public_file_server = ActiveSupport::OrderedOptions.new
  35. 17 @public_file_server.enabled = true
  36. 17 @public_file_server.index_name = "index"
  37. 17 @force_ssl = false
  38. 17 @ssl_options = {}
  39. 17 @session_store = nil
  40. 17 @time_zone = "UTC"
  41. 17 @beginning_of_week = :monday
  42. 17 @log_level = :debug
  43. 17 @generators = app_generators
  44. 17 @cache_store = [ :file_store, "#{root}/tmp/cache/" ]
  45. 17 @railties_order = [:all]
  46. 17 @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"]
  47. 17 @reload_classes_only_on_change = true
  48. 17 @file_watcher = ActiveSupport::FileUpdateChecker
  49. 17 @exceptions_app = nil
  50. 17 @autoflush_log = true
  51. 17 @log_formatter = ActiveSupport::Logger::SimpleFormatter.new
  52. 17 @eager_load = nil
  53. 17 @secret_key_base = nil
  54. 17 @api_only = false
  55. 17 @debug_exception_response_format = nil
  56. 17 @x = Custom.new
  57. 17 @enable_dependency_loading = false
  58. 17 @read_encrypted_secrets = false
  59. 17 @content_security_policy = nil
  60. 17 @content_security_policy_report_only = false
  61. 17 @content_security_policy_nonce_generator = nil
  62. 17 @content_security_policy_nonce_directives = nil
  63. 17 @require_master_key = false
  64. 17 @loaded_config_version = nil
  65. 17 @credentials = ActiveSupport::OrderedOptions.new
  66. 17 @credentials.content_path = default_credentials_content_path
  67. 17 @credentials.key_path = default_credentials_key_path
  68. 17 @autoloader = :classic
  69. 17 @disable_sandbox = false
  70. 17 @add_autoload_paths_to_load_path = true
  71. 17 @feature_policy = nil
  72. 17 @rake_eager_load = false
  73. end
  74. # Loads default configurations. See {the result of the method for each version}[https://guides.rubyonrails.org/configuring.html#results-of-config-load-defaults].
  75. 17 def load_defaults(target_version)
  76. case target_version.to_s
  77. when "5.0"
  78. if respond_to?(:action_controller)
  79. action_controller.per_form_csrf_tokens = true
  80. action_controller.forgery_protection_origin_check = true
  81. end
  82. ActiveSupport.to_time_preserves_timezone = true
  83. if respond_to?(:active_record)
  84. active_record.belongs_to_required_by_default = true
  85. end
  86. self.ssl_options = { hsts: { subdomains: true } }
  87. when "5.1"
  88. load_defaults "5.0"
  89. if respond_to?(:assets)
  90. assets.unknown_asset_fallback = false
  91. end
  92. if respond_to?(:action_view)
  93. action_view.form_with_generates_remote_forms = true
  94. end
  95. when "5.2"
  96. load_defaults "5.1"
  97. if respond_to?(:active_record)
  98. active_record.cache_versioning = true
  99. end
  100. if respond_to?(:action_dispatch)
  101. action_dispatch.use_authenticated_cookie_encryption = true
  102. end
  103. if respond_to?(:active_support)
  104. active_support.use_authenticated_message_encryption = true
  105. active_support.use_sha1_digests = true
  106. end
  107. if respond_to?(:action_controller)
  108. action_controller.default_protect_from_forgery = true
  109. end
  110. if respond_to?(:action_view)
  111. action_view.form_with_generates_ids = true
  112. end
  113. when "6.0"
  114. load_defaults "5.2"
  115. self.autoloader = :zeitwerk if RUBY_ENGINE == "ruby"
  116. if respond_to?(:action_view)
  117. action_view.default_enforce_utf8 = false
  118. end
  119. if respond_to?(:action_dispatch)
  120. action_dispatch.use_cookies_with_metadata = true
  121. action_dispatch.return_only_media_type_on_content_type = false
  122. end
  123. if respond_to?(:action_mailer)
  124. action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
  125. end
  126. if respond_to?(:active_job)
  127. active_job.return_false_on_aborted_enqueue = true
  128. end
  129. if respond_to?(:active_storage)
  130. active_storage.queues.analysis = :active_storage_analysis
  131. active_storage.queues.purge = :active_storage_purge
  132. active_storage.replace_on_assign_to_many = true
  133. end
  134. if respond_to?(:active_record)
  135. active_record.collection_cache_versioning = true
  136. end
  137. when "6.1"
  138. load_defaults "6.0"
  139. if respond_to?(:active_record)
  140. active_record.has_many_inversing = true
  141. end
  142. if respond_to?(:active_storage)
  143. active_storage.track_variants = true
  144. end
  145. if respond_to?(:active_job)
  146. active_job.retry_jitter = 0.15
  147. active_job.skip_after_callbacks_if_terminated = true
  148. end
  149. if respond_to?(:action_dispatch)
  150. action_dispatch.cookies_same_site_protection = :lax
  151. action_dispatch.ssl_default_redirect_status = 308
  152. end
  153. if respond_to?(:action_controller)
  154. action_controller.urlsafe_csrf_tokens = true
  155. end
  156. ActiveSupport.utc_to_local_returns_utc_offset_times = true
  157. else
  158. raise "Unknown version #{target_version.to_s.inspect}"
  159. end
  160. @loaded_config_version = target_version
  161. end
  162. 17 def encoding=(value)
  163. 17 @encoding = value
  164. 17 silence_warnings do
  165. 17 Encoding.default_external = value
  166. 17 Encoding.default_internal = value
  167. end
  168. end
  169. 17 def api_only=(value)
  170. @api_only = value
  171. generators.api_only = value
  172. @debug_exception_response_format ||= :api
  173. end
  174. 17 def debug_exception_response_format
  175. @debug_exception_response_format || :default
  176. end
  177. 17 attr_writer :debug_exception_response_format
  178. 17 def paths
  179. 33 @paths ||= begin
  180. 17 paths = super
  181. 17 paths.add "config/database", with: "config/database.yml"
  182. 17 paths.add "config/secrets", with: "config", glob: "secrets.yml{,.enc}"
  183. 17 paths.add "config/environment", with: "config/environment.rb"
  184. 17 paths.add "lib/templates"
  185. 17 paths.add "log", with: "log/#{Rails.env}.log"
  186. 17 paths.add "public"
  187. 17 paths.add "public/javascripts"
  188. 17 paths.add "public/stylesheets"
  189. 17 paths.add "tmp"
  190. 17 paths
  191. end
  192. end
  193. # Load the database YAML without evaluating ERB. This allows us to
  194. # create the rake tasks for multiple databases without filling in the
  195. # configuration values or loading the environment. Do not use this
  196. # method.
  197. #
  198. # This uses a DummyERB custom compiler so YAML can ignore the ERB
  199. # tags and load the database.yml for the rake tasks.
  200. 17 def load_database_yaml # :nodoc:
  201. if path = paths["config/database"].existent.first
  202. require "rails/application/dummy_erb_compiler"
  203. yaml = Pathname.new(path)
  204. erb = DummyERB.new(yaml.read)
  205. YAML.load(erb.result) || {}
  206. else
  207. {}
  208. end
  209. end
  210. # Loads and returns the entire raw configuration of database from
  211. # values stored in <tt>config/database.yml</tt>.
  212. 17 def database_configuration
  213. path = paths["config/database"].existent.first
  214. yaml = Pathname.new(path) if path
  215. config = if yaml&.exist?
  216. loaded_yaml = ActiveSupport::ConfigurationFile.parse(yaml)
  217. if (shared = loaded_yaml.delete("shared"))
  218. loaded_yaml.each do |_k, values|
  219. values.reverse_merge!(shared)
  220. end
  221. end
  222. Hash.new(shared).merge(loaded_yaml)
  223. elsif ENV["DATABASE_URL"]
  224. # Value from ENV['DATABASE_URL'] is set to default database connection
  225. # by Active Record.
  226. {}
  227. else
  228. raise "Could not load database configuration. No such file - #{paths["config/database"].instance_variable_get(:@paths)}"
  229. end
  230. config
  231. rescue => e
  232. raise e, "Cannot load database configuration:\n#{e.message}", e.backtrace
  233. end
  234. 17 def colorize_logging
  235. ActiveSupport::LogSubscriber.colorize_logging
  236. end
  237. 17 def colorize_logging=(val)
  238. ActiveSupport::LogSubscriber.colorize_logging = val
  239. generators.colorize_logging = val
  240. end
  241. 17 def session_store(new_session_store = nil, **options)
  242. if new_session_store
  243. if new_session_store == :active_record_store
  244. begin
  245. ActionDispatch::Session::ActiveRecordStore
  246. rescue NameError
  247. raise "`ActiveRecord::SessionStore` is extracted out of Rails into a gem. " \
  248. "Please add `activerecord-session_store` to your Gemfile to use it."
  249. end
  250. end
  251. @session_store = new_session_store
  252. @session_options = options || {}
  253. else
  254. case @session_store
  255. when :disabled
  256. nil
  257. when :active_record_store
  258. ActionDispatch::Session::ActiveRecordStore
  259. when Symbol
  260. ActionDispatch::Session.const_get(@session_store.to_s.camelize)
  261. else
  262. @session_store
  263. end
  264. end
  265. end
  266. 17 def session_store? #:nodoc:
  267. @session_store
  268. end
  269. 17 def annotations
  270. Rails::SourceAnnotationExtractor::Annotation
  271. end
  272. 17 def content_security_policy(&block)
  273. if block_given?
  274. @content_security_policy = ActionDispatch::ContentSecurityPolicy.new(&block)
  275. else
  276. @content_security_policy
  277. end
  278. end
  279. 17 def feature_policy(&block)
  280. if block_given?
  281. @feature_policy = ActionDispatch::FeaturePolicy.new(&block)
  282. else
  283. @feature_policy
  284. end
  285. end
  286. 17 def autoloader=(autoloader)
  287. case autoloader
  288. when :classic
  289. @autoloader = autoloader
  290. when :zeitwerk
  291. require "zeitwerk"
  292. @autoloader = autoloader
  293. else
  294. raise ArgumentError, "config.autoloader may be :classic or :zeitwerk, got #{autoloader.inspect} instead"
  295. end
  296. end
  297. 17 def default_log_file
  298. path = paths["log"].first
  299. unless File.exist? File.dirname path
  300. FileUtils.mkdir_p File.dirname path
  301. end
  302. f = File.open path, "a"
  303. f.binmode
  304. f.sync = autoflush_log # if true make sure every write flushes
  305. f
  306. end
  307. 17 class Custom #:nodoc:
  308. 17 def initialize
  309. 17 @configurations = Hash.new
  310. end
  311. 17 def method_missing(method, *args)
  312. if method.end_with?("=")
  313. @configurations[:"#{method[0..-2]}"] = args.first
  314. else
  315. @configurations.fetch(method) {
  316. @configurations[method] = ActiveSupport::OrderedOptions.new
  317. }
  318. end
  319. end
  320. 17 def respond_to_missing?(symbol, *)
  321. true
  322. end
  323. end
  324. 17 private
  325. 17 def default_credentials_content_path
  326. 17 if credentials_available_for_current_env?
  327. root.join("config", "credentials", "#{Rails.env}.yml.enc")
  328. else
  329. 17 root.join("config", "credentials.yml.enc")
  330. end
  331. end
  332. 17 def default_credentials_key_path
  333. 17 if credentials_available_for_current_env?
  334. root.join("config", "credentials", "#{Rails.env}.key")
  335. else
  336. 17 root.join("config", "master.key")
  337. end
  338. end
  339. 17 def credentials_available_for_current_env?
  340. 34 File.exist?(root.join("config", "credentials", "#{Rails.env}.yml.enc"))
  341. end
  342. end
  343. end
  344. end

lib/rails/application/default_middleware_stack.rb

0.0% lines covered

85 relevant lines. 0 lines covered and 85 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. class Application
  4. class DefaultMiddlewareStack
  5. attr_reader :config, :paths, :app
  6. def initialize(app, config, paths)
  7. @app = app
  8. @config = config
  9. @paths = paths
  10. end
  11. def build_stack
  12. ActionDispatch::MiddlewareStack.new do |middleware|
  13. middleware.use ::ActionDispatch::HostAuthorization, config.hosts, config.action_dispatch.hosts_response_app
  14. if config.force_ssl
  15. middleware.use ::ActionDispatch::SSL, **config.ssl_options,
  16. ssl_default_redirect_status: config.action_dispatch.ssl_default_redirect_status
  17. end
  18. middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
  19. if config.public_file_server.enabled
  20. headers = config.public_file_server.headers || {}
  21. middleware.use ::ActionDispatch::Static, paths["public"].first, index: config.public_file_server.index_name, headers: headers
  22. end
  23. if rack_cache = load_rack_cache
  24. require "action_dispatch/http/rack_cache"
  25. middleware.use ::Rack::Cache, rack_cache
  26. end
  27. if config.allow_concurrency == false
  28. # User has explicitly opted out of concurrent request
  29. # handling: presumably their code is not threadsafe
  30. middleware.use ::Rack::Lock
  31. end
  32. middleware.use ::ActionDispatch::Executor, app.executor
  33. middleware.use ::Rack::Runtime
  34. middleware.use ::Rack::MethodOverride unless config.api_only
  35. middleware.use ::ActionDispatch::RequestId
  36. middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
  37. middleware.use ::Rails::Rack::Logger, config.log_tags
  38. middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
  39. middleware.use ::ActionDispatch::DebugExceptions, app, config.debug_exception_response_format
  40. middleware.use ::ActionDispatch::ActionableExceptions
  41. unless config.cache_classes
  42. middleware.use ::ActionDispatch::Reloader, app.reloader
  43. end
  44. middleware.use ::ActionDispatch::Callbacks
  45. middleware.use ::ActionDispatch::Cookies unless config.api_only
  46. if !config.api_only && config.session_store
  47. if config.force_ssl && config.ssl_options.fetch(:secure_cookies, true) && !config.session_options.key?(:secure)
  48. config.session_options[:secure] = true
  49. end
  50. middleware.use config.session_store, config.session_options
  51. middleware.use ::ActionDispatch::Flash
  52. end
  53. unless config.api_only
  54. middleware.use ::ActionDispatch::ContentSecurityPolicy::Middleware
  55. middleware.use ::ActionDispatch::FeaturePolicy::Middleware
  56. end
  57. middleware.use ::Rack::Head
  58. middleware.use ::Rack::ConditionalGet
  59. middleware.use ::Rack::ETag, "no-cache"
  60. middleware.use ::Rack::TempfileReaper unless config.api_only
  61. end
  62. end
  63. private
  64. def load_rack_cache
  65. rack_cache = config.action_dispatch.rack_cache
  66. return unless rack_cache
  67. begin
  68. require "rack/cache"
  69. rescue LoadError => error
  70. error.message << " Be sure to add rack-cache to your Gemfile"
  71. raise
  72. end
  73. if rack_cache == true
  74. {
  75. metastore: "rails:/",
  76. entitystore: "rails:/",
  77. verbose: false
  78. }
  79. else
  80. rack_cache
  81. end
  82. end
  83. def show_exceptions_app
  84. config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
  85. end
  86. end
  87. end
  88. end

lib/rails/application/dummy_erb_compiler.rb

0.0% lines covered

12 relevant lines. 0 lines covered and 12 lines missed.
    
  1. # frozen_string_literal: true
  2. # These classes are used to strip out the ERB configuration
  3. # values so we can evaluate the database.yml without evaluating
  4. # the ERB values.
  5. class DummyERB < ERB # :nodoc:
  6. def make_compiler(trim_mode)
  7. DummyCompiler.new trim_mode
  8. end
  9. end
  10. class DummyCompiler < ERB::Compiler # :nodoc:
  11. def compile_content(stag, out)
  12. if stag == "<%="
  13. out.push "_erbout << ''"
  14. end
  15. end
  16. end

lib/rails/application/finisher.rb

0.0% lines covered

177 relevant lines. 0 lines covered and 177 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/core_ext/string/inflections"
  3. require "active_support/core_ext/array/conversions"
  4. module Rails
  5. class Application
  6. module Finisher
  7. include Initializable
  8. initializer :add_generator_templates do
  9. config.generators.templates.unshift(*paths["lib/templates"].existent)
  10. end
  11. initializer :ensure_autoload_once_paths_as_subset do
  12. extra = ActiveSupport::Dependencies.autoload_once_paths -
  13. ActiveSupport::Dependencies.autoload_paths
  14. unless extra.empty?
  15. abort <<-end_error
  16. autoload_once_paths must be a subset of the autoload_paths.
  17. Extra items in autoload_once_paths: #{extra * ','}
  18. end_error
  19. end
  20. end
  21. # This will become an error if/when we remove classic mode. The plan is
  22. # autoloaders won't be configured up to this point in the finisher, so
  23. # constants just won't be found, raising regular NameError exceptions.
  24. initializer :warn_if_autoloaded, before: :let_zeitwerk_take_over do
  25. next if config.cache_classes
  26. next if ActiveSupport::Dependencies.autoloaded_constants.empty?
  27. autoloaded = ActiveSupport::Dependencies.autoloaded_constants
  28. constants = "constant".pluralize(autoloaded.size)
  29. enum = autoloaded.to_sentence
  30. have = autoloaded.size == 1 ? "has" : "have"
  31. these = autoloaded.size == 1 ? "This" : "These"
  32. example = autoloaded.first
  33. example_klass = example.constantize.class
  34. if config.autoloader == :zeitwerk
  35. ActiveSupport::DescendantsTracker.clear
  36. ActiveSupport::Dependencies.clear
  37. unload_message = "#{these} autoloaded #{constants} #{have} been unloaded."
  38. else
  39. unload_message = "`config.autoloader` is set to `#{config.autoloader}`. #{these} autoloaded #{constants} would have been unloaded if `config.autoloader` had been set to `:zeitwerk`."
  40. end
  41. ActiveSupport::Deprecation.warn(<<~WARNING)
  42. Initialization autoloaded the #{constants} #{enum}.
  43. Being able to do this is deprecated. Autoloading during initialization is going
  44. to be an error condition in future versions of Rails.
  45. Reloading does not reboot the application, and therefore code executed during
  46. initialization does not run again. So, if you reload #{example}, for example,
  47. the expected changes won't be reflected in that stale #{example_klass} object.
  48. #{unload_message}
  49. In order to autoload safely at boot time, please wrap your code in a reloader
  50. callback this way:
  51. Rails.application.reloader.to_prepare do
  52. # Autoload classes and modules needed at boot time here.
  53. end
  54. That block runs when the application boots, and every time there is a reload.
  55. For historical reasons, it may run twice, so it has to be idempotent.
  56. Check the "Autoloading and Reloading Constants" guide to learn more about how
  57. Rails autoloads and reloads.
  58. WARNING
  59. end
  60. initializer :let_zeitwerk_take_over do
  61. if config.autoloader == :zeitwerk
  62. require "active_support/dependencies/zeitwerk_integration"
  63. ActiveSupport::Dependencies::ZeitwerkIntegration.take_over(enable_reloading: !config.cache_classes)
  64. end
  65. end
  66. initializer :add_builtin_route do |app|
  67. if Rails.env.development?
  68. app.routes.prepend do
  69. get "/rails/info/properties" => "rails/info#properties", internal: true
  70. get "/rails/info/routes" => "rails/info#routes", internal: true
  71. get "/rails/info" => "rails/info#index", internal: true
  72. end
  73. app.routes.append do
  74. get "/" => "rails/welcome#index", internal: true
  75. end
  76. end
  77. end
  78. # Setup default session store if not already set in config/application.rb
  79. initializer :setup_default_session_store, before: :build_middleware_stack do |app|
  80. unless app.config.session_store?
  81. app_name = app.class.name ? app.railtie_name.chomp("_application") : ""
  82. app.config.session_store :cookie_store, key: "_#{app_name}_session"
  83. end
  84. end
  85. initializer :build_middleware_stack do
  86. build_middleware_stack
  87. end
  88. initializer :define_main_app_helper do |app|
  89. app.routes.define_mounted_helper(:main_app)
  90. end
  91. initializer :add_to_prepare_blocks do |app|
  92. config.to_prepare_blocks.each do |block|
  93. app.reloader.to_prepare(&block)
  94. end
  95. end
  96. # This needs to happen before eager load so it happens
  97. # in exactly the same point regardless of config.eager_load
  98. initializer :run_prepare_callbacks do |app|
  99. app.reloader.prepare!
  100. end
  101. initializer :eager_load! do
  102. if config.eager_load
  103. ActiveSupport.run_load_hooks(:before_eager_load, self)
  104. # Checks defined?(Zeitwerk) instead of zeitwerk_enabled? because we
  105. # want to eager load any dependency managed by Zeitwerk regardless of
  106. # the autoloading mode of the application.
  107. Zeitwerk::Loader.eager_load_all if defined?(Zeitwerk)
  108. config.eager_load_namespaces.each(&:eager_load!)
  109. end
  110. end
  111. # All initialization is done, including eager loading in production
  112. initializer :finisher_hook do
  113. ActiveSupport.run_load_hooks(:after_initialize, self)
  114. end
  115. class MutexHook
  116. def initialize(mutex = Mutex.new)
  117. @mutex = mutex
  118. end
  119. def run
  120. @mutex.lock
  121. end
  122. def complete(_state)
  123. @mutex.unlock
  124. end
  125. end
  126. module InterlockHook
  127. def self.run
  128. ActiveSupport::Dependencies.interlock.start_running
  129. end
  130. def self.complete(_state)
  131. ActiveSupport::Dependencies.interlock.done_running
  132. end
  133. end
  134. initializer :configure_executor_for_concurrency do |app|
  135. if config.allow_concurrency == false
  136. # User has explicitly opted out of concurrent request
  137. # handling: presumably their code is not threadsafe
  138. app.executor.register_hook(MutexHook.new, outer: true)
  139. elsif config.allow_concurrency == :unsafe
  140. # Do nothing, even if we know this is dangerous. This is the
  141. # historical behavior for true.
  142. else
  143. # Default concurrency setting: enabled, but safe
  144. unless config.cache_classes && config.eager_load
  145. # Without cache_classes + eager_load, the load interlock
  146. # is required for proper operation
  147. app.executor.register_hook(InterlockHook, outer: true)
  148. end
  149. end
  150. end
  151. # Set routes reload after the finisher hook to ensure routes added in
  152. # the hook are taken into account.
  153. initializer :set_routes_reloader_hook do |app|
  154. reloader = routes_reloader
  155. reloader.eager_load = app.config.eager_load
  156. reloader.execute
  157. reloaders << reloader
  158. app.reloader.to_run do
  159. # We configure #execute rather than #execute_if_updated because if
  160. # autoloaded constants are cleared we need to reload routes also in
  161. # case any was used there, as in
  162. #
  163. # mount MailPreview => 'mail_view'
  164. #
  165. # This means routes are also reloaded if i18n is updated, which
  166. # might not be necessary, but in order to be more precise we need
  167. # some sort of reloaders dependency support, to be added.
  168. require_unload_lock!
  169. reloader.execute
  170. end
  171. end
  172. # Set clearing dependencies after the finisher hook to ensure paths
  173. # added in the hook are taken into account.
  174. initializer :set_clear_dependencies_hook, group: :all do |app|
  175. callback = lambda do
  176. ActiveSupport::DescendantsTracker.clear
  177. ActiveSupport::Dependencies.clear
  178. end
  179. if config.cache_classes
  180. app.reloader.check = lambda { false }
  181. elsif config.reload_classes_only_on_change
  182. app.reloader.check = lambda do
  183. app.reloaders.map(&:updated?).any?
  184. end
  185. else
  186. app.reloader.check = lambda { true }
  187. end
  188. if config.cache_classes
  189. # No reloader
  190. elsif config.reload_classes_only_on_change
  191. reloader = config.file_watcher.new(*watchable_args, &callback)
  192. reloaders << reloader
  193. # Prepend this callback to have autoloaded constants cleared before
  194. # any other possible reloading, in case they need to autoload fresh
  195. # constants.
  196. app.reloader.to_run(prepend: true) do
  197. # In addition to changes detected by the file watcher, if routes
  198. # or i18n have been updated we also need to clear constants,
  199. # that's why we run #execute rather than #execute_if_updated, this
  200. # callback has to clear autoloaded constants after any update.
  201. class_unload! do
  202. reloader.execute
  203. end
  204. end
  205. else
  206. app.reloader.to_complete do
  207. class_unload!(&callback)
  208. end
  209. end
  210. end
  211. # Disable dependency loading during request cycle
  212. initializer :disable_dependency_loading do
  213. if config.eager_load && config.cache_classes && !config.enable_dependency_loading
  214. ActiveSupport::Dependencies.unhook!
  215. end
  216. end
  217. end
  218. end
  219. end

lib/rails/application/routes_reloader.rb

0.0% lines covered

50 relevant lines. 0 lines covered and 50 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/core_ext/module/delegation"
  3. module Rails
  4. class Application
  5. class RoutesReloader
  6. attr_reader :route_sets, :paths, :external_routes
  7. attr_accessor :eager_load
  8. delegate :execute_if_updated, :execute, :updated?, to: :updater
  9. def initialize
  10. @paths = []
  11. @route_sets = []
  12. @external_routes = []
  13. @eager_load = false
  14. end
  15. def reload!
  16. clear!
  17. load_paths
  18. finalize!
  19. route_sets.each(&:eager_load!) if eager_load
  20. ensure
  21. revert
  22. end
  23. private
  24. def updater
  25. @updater ||= begin
  26. dirs = @external_routes.each_with_object({}) do |dir, hash|
  27. hash[dir.to_s] = %w(rb)
  28. end
  29. ActiveSupport::FileUpdateChecker.new(paths, dirs) { reload! }
  30. end
  31. end
  32. def clear!
  33. route_sets.each do |routes|
  34. routes.disable_clear_and_finalize = true
  35. routes.clear!
  36. end
  37. end
  38. def load_paths
  39. paths.each { |path| load(path) }
  40. end
  41. def finalize!
  42. route_sets.each(&:finalize!)
  43. end
  44. def revert
  45. route_sets.each do |routes|
  46. routes.disable_clear_and_finalize = false
  47. end
  48. end
  49. end
  50. end
  51. end

lib/rails/application_controller.rb

0.0% lines covered

21 relevant lines. 0 lines covered and 21 lines missed.
    
  1. # frozen_string_literal: true
  2. class Rails::ApplicationController < ActionController::Base # :nodoc:
  3. self.view_paths = File.expand_path("templates", __dir__)
  4. layout "application"
  5. before_action :disable_content_security_policy_nonce!
  6. content_security_policy do |policy|
  7. policy.script_src :unsafe_inline
  8. policy.style_src :unsafe_inline
  9. end
  10. private
  11. def require_local!
  12. unless local_request?
  13. render html: "<p>For security purposes, this information is only available to local requests.</p>".html_safe, status: :forbidden
  14. end
  15. end
  16. def local_request?
  17. Rails.application.config.consider_all_requests_local || request.local?
  18. end
  19. def disable_content_security_policy_nonce!
  20. request.content_security_policy_nonce_generator = nil
  21. end
  22. end

lib/rails/autoloaders.rb

0.0% lines covered

39 relevant lines. 0 lines covered and 39 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/dependencies/zeitwerk_integration"
  3. module Rails
  4. module Autoloaders # :nodoc:
  5. class << self
  6. include Enumerable
  7. def main
  8. if zeitwerk_enabled?
  9. @main ||= Zeitwerk::Loader.new.tap do |loader|
  10. loader.tag = "rails.main"
  11. loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
  12. end
  13. end
  14. end
  15. def once
  16. if zeitwerk_enabled?
  17. @once ||= Zeitwerk::Loader.new.tap do |loader|
  18. loader.tag = "rails.once"
  19. loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
  20. end
  21. end
  22. end
  23. def each
  24. if zeitwerk_enabled?
  25. yield main
  26. yield once
  27. end
  28. end
  29. def logger=(logger)
  30. each { |loader| loader.logger = logger }
  31. end
  32. def log!
  33. each(&:log!)
  34. end
  35. def zeitwerk_enabled?
  36. Rails.configuration.autoloader == :zeitwerk
  37. end
  38. end
  39. end
  40. end

lib/rails/backtrace_cleaner.rb

0.0% lines covered

23 relevant lines. 0 lines covered and 23 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/backtrace_cleaner"
  3. require "active_support/core_ext/string/access"
  4. module Rails
  5. class BacktraceCleaner < ActiveSupport::BacktraceCleaner
  6. APP_DIRS_PATTERN = /\A(?:\.\/)?(?:app|config|lib|test|\(\w*\))/
  7. RENDER_TEMPLATE_PATTERN = /:in `.*_\w+_{2,3}\d+_\d+'/
  8. def initialize
  9. super
  10. @root = "#{Rails.root}/"
  11. add_filter do |line|
  12. line.start_with?(@root) ? line.from(@root.size) : line
  13. end
  14. add_filter do |line|
  15. if RENDER_TEMPLATE_PATTERN.match?(line)
  16. line.sub(RENDER_TEMPLATE_PATTERN, "")
  17. else
  18. line
  19. end
  20. end
  21. add_silencer { |line| !APP_DIRS_PATTERN.match?(line) }
  22. end
  23. end
  24. end

lib/rails/cli.rb

0.0% lines covered

11 relevant lines. 0 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/app_loader"
  3. # If we are inside a Rails application this method performs an exec and thus
  4. # the rest of this script is not run.
  5. Rails::AppLoader.exec_app
  6. require "rails/ruby_version_check"
  7. Signal.trap("INT") { puts; exit(1) }
  8. require "rails/command"
  9. if ARGV.first == "plugin"
  10. ARGV.shift
  11. Rails::Command.invoke :plugin, ARGV
  12. else
  13. Rails::Command.invoke :application, ARGV
  14. end

lib/rails/code_statistics.rb

0.0% lines covered

94 relevant lines. 0 lines covered and 94 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/code_statistics_calculator"
  3. require "active_support/core_ext/enumerable"
  4. class CodeStatistics #:nodoc:
  5. TEST_TYPES = ["Controller tests",
  6. "Helper tests",
  7. "Model tests",
  8. "Mailer tests",
  9. "Mailbox tests",
  10. "Channel tests",
  11. "Job tests",
  12. "Integration tests",
  13. "System tests"]
  14. HEADERS = { lines: " Lines", code_lines: " LOC", classes: "Classes", methods: "Methods" }
  15. def initialize(*pairs)
  16. @pairs = pairs
  17. @statistics = calculate_statistics
  18. @total = calculate_total if pairs.length > 1
  19. end
  20. def to_s
  21. print_header
  22. @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
  23. print_splitter
  24. if @total
  25. print_line("Total", @total)
  26. print_splitter
  27. end
  28. print_code_test_stats
  29. end
  30. private
  31. def calculate_statistics
  32. Hash[@pairs.map { |pair| [pair.first, calculate_directory_statistics(pair.last)] }]
  33. end
  34. def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|ts|coffee|rake)$/)
  35. stats = CodeStatisticsCalculator.new
  36. Dir.foreach(directory) do |file_name|
  37. path = "#{directory}/#{file_name}"
  38. if File.directory?(path) && !file_name.start_with?(".")
  39. stats.add(calculate_directory_statistics(path, pattern))
  40. elsif file_name&.match?(pattern)
  41. stats.add_by_file_path(path)
  42. end
  43. end
  44. stats
  45. end
  46. def calculate_total
  47. @statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, total|
  48. total.add(pair.last)
  49. end
  50. end
  51. def calculate_code
  52. code_loc = 0
  53. @statistics.each { |k, v| code_loc += v.code_lines unless TEST_TYPES.include? k }
  54. code_loc
  55. end
  56. def calculate_tests
  57. test_loc = 0
  58. @statistics.each { |k, v| test_loc += v.code_lines if TEST_TYPES.include? k }
  59. test_loc
  60. end
  61. def width_for(label)
  62. [@statistics.values.sum { |s| s.send(label) }.to_s.size, HEADERS[label].length].max
  63. end
  64. def print_header
  65. print_splitter
  66. print "| Name "
  67. HEADERS.each do |k, v|
  68. print " | #{v.rjust(width_for(k))}"
  69. end
  70. puts " | M/C | LOC/M |"
  71. print_splitter
  72. end
  73. def print_splitter
  74. print "+----------------------"
  75. HEADERS.each_key do |k|
  76. print "+#{'-' * (width_for(k) + 2)}"
  77. end
  78. puts "+-----+-------+"
  79. end
  80. def print_line(name, statistics)
  81. m_over_c = (statistics.methods / statistics.classes) rescue 0
  82. loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue 0
  83. print "| #{name.ljust(20)} "
  84. HEADERS.each_key do |k|
  85. print "| #{statistics.send(k).to_s.rjust(width_for(k))} "
  86. end
  87. puts "| #{m_over_c.to_s.rjust(3)} | #{loc_over_m.to_s.rjust(5)} |"
  88. end
  89. def print_code_test_stats
  90. code = calculate_code
  91. tests = calculate_tests
  92. puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f / code)}"
  93. puts ""
  94. end
  95. end

lib/rails/code_statistics_calculator.rb

0.0% lines covered

75 relevant lines. 0 lines covered and 75 lines missed.
    
  1. # frozen_string_literal: true
  2. class CodeStatisticsCalculator #:nodoc:
  3. attr_reader :lines, :code_lines, :classes, :methods
  4. PATTERNS = {
  5. rb: {
  6. line_comment: /^\s*#/,
  7. begin_block_comment: /^=begin/,
  8. end_block_comment: /^=end/,
  9. class: /^\s*class\s+[_A-Z]/,
  10. method: /^\s*def\s+[_a-z]/,
  11. },
  12. js: {
  13. line_comment: %r{^\s*//},
  14. begin_block_comment: %r{^\s*/\*},
  15. end_block_comment: %r{\*/},
  16. method: /function(\s+[_a-zA-Z][\da-zA-Z]*)?\s*\(/,
  17. },
  18. coffee: {
  19. line_comment: /^\s*#/,
  20. begin_block_comment: /^\s*###/,
  21. end_block_comment: /^\s*###/,
  22. class: /^\s*class\s+[_A-Z]/,
  23. method: /[-=]>/,
  24. }
  25. }
  26. PATTERNS[:minitest] = PATTERNS[:rb].merge method: /^\s*(def|test)\s+['"_a-z]/
  27. PATTERNS[:rake] = PATTERNS[:rb]
  28. def initialize(lines = 0, code_lines = 0, classes = 0, methods = 0)
  29. @lines = lines
  30. @code_lines = code_lines
  31. @classes = classes
  32. @methods = methods
  33. end
  34. def add(code_statistics_calculator)
  35. @lines += code_statistics_calculator.lines
  36. @code_lines += code_statistics_calculator.code_lines
  37. @classes += code_statistics_calculator.classes
  38. @methods += code_statistics_calculator.methods
  39. end
  40. def add_by_file_path(file_path)
  41. File.open(file_path) do |f|
  42. add_by_io(f, file_type(file_path))
  43. end
  44. end
  45. def add_by_io(io, file_type)
  46. patterns = PATTERNS[file_type] || {}
  47. comment_started = false
  48. while line = io.gets
  49. @lines += 1
  50. if comment_started
  51. if patterns[:end_block_comment] && patterns[:end_block_comment].match?(line)
  52. comment_started = false
  53. end
  54. next
  55. else
  56. if patterns[:begin_block_comment] && patterns[:begin_block_comment].match?(line)
  57. comment_started = true
  58. next
  59. end
  60. end
  61. @classes += 1 if patterns[:class] && patterns[:class].match?(line)
  62. @methods += 1 if patterns[:method] && patterns[:method].match?(line)
  63. if !line.match?(/^\s*$/) && (patterns[:line_comment].nil? || !line.match?(patterns[:line_comment]))
  64. @code_lines += 1
  65. end
  66. end
  67. end
  68. private
  69. def file_type(file_path)
  70. if file_path.end_with? "_test.rb"
  71. :minitest
  72. else
  73. File.extname(file_path).delete_prefix(".").downcase.to_sym
  74. end
  75. end
  76. end

lib/rails/command.rb

44.07% lines covered

59 relevant lines. 26 lines covered and 33 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 require "active_support"
  3. 16 require "active_support/core_ext/enumerable"
  4. 16 require "active_support/core_ext/object/blank"
  5. 16 require "thor"
  6. 16 module Rails
  7. 16 module Command
  8. 16 extend ActiveSupport::Autoload
  9. 16 autoload :Spellchecker
  10. 16 autoload :Behavior
  11. 16 autoload :Base
  12. 16 include Behavior
  13. 16 HELP_MAPPINGS = %w(-h -? --help)
  14. 16 class << self
  15. 16 def hidden_commands # :nodoc:
  16. @hidden_commands ||= []
  17. end
  18. 16 def environment # :nodoc:
  19. ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development"
  20. end
  21. # Receives a namespace, arguments and the behavior to invoke the command.
  22. 16 def invoke(full_namespace, args = [], **config)
  23. namespace = full_namespace = full_namespace.to_s
  24. if char = namespace =~ /:(\w+)$/
  25. command_name, namespace = $1, namespace.slice(0, char)
  26. else
  27. command_name = namespace
  28. end
  29. command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)
  30. command_name, namespace = "version", "version" if %w( -v --version ).include?(command_name)
  31. # isolate ARGV to ensure that commands depend only on the args they are given
  32. args = args.dup # args might *be* ARGV so dup before clearing
  33. old_argv = ARGV.dup
  34. ARGV.clear
  35. command = find_by_namespace(namespace, command_name)
  36. if command && command.all_commands[command_name]
  37. command.perform(command_name, args, config)
  38. else
  39. find_by_namespace("rake").perform(full_namespace, args, config)
  40. end
  41. ensure
  42. ARGV.replace(old_argv)
  43. end
  44. # Rails finds namespaces similar to Thor, it only adds one rule:
  45. #
  46. # Command names must end with "_command.rb". This is required because Rails
  47. # looks in load paths and loads the command just before it's going to be used.
  48. #
  49. # find_by_namespace :webrat, :rails, :integration
  50. #
  51. # Will search for the following commands:
  52. #
  53. # "rails:webrat", "webrat:integration", "webrat"
  54. #
  55. # Notice that "rails:commands:webrat" could be loaded as well, what
  56. # Rails looks for is the first and last parts of the namespace.
  57. 16 def find_by_namespace(namespace, command_name = nil) # :nodoc:
  58. lookups = [ namespace ]
  59. lookups << "#{namespace}:#{command_name}" if command_name
  60. lookups.concat lookups.map { |lookup| "rails:#{lookup}" }
  61. lookup(lookups)
  62. namespaces = subclasses.index_by(&:namespace)
  63. namespaces[(lookups & namespaces.keys).first]
  64. end
  65. # Returns the root of the Rails engine or app running the command.
  66. 16 def root
  67. if defined?(ENGINE_ROOT)
  68. Pathname.new(ENGINE_ROOT)
  69. elsif defined?(APP_PATH)
  70. Pathname.new(File.expand_path("../..", APP_PATH))
  71. end
  72. end
  73. 16 def print_commands # :nodoc:
  74. commands.each { |command| puts(" #{command}") }
  75. end
  76. 16 private
  77. 16 COMMANDS_IN_USAGE = %w(generate console server test test:system dbconsole new)
  78. 16 private_constant :COMMANDS_IN_USAGE
  79. 16 def commands
  80. lookup!
  81. visible_commands = (subclasses - hidden_commands).flat_map(&:printing_commands)
  82. (visible_commands - COMMANDS_IN_USAGE).sort
  83. end
  84. 16 def command_type # :doc:
  85. @command_type ||= "command"
  86. end
  87. 16 def lookup_paths # :doc:
  88. @lookup_paths ||= %w( rails/commands commands )
  89. end
  90. 16 def file_lookup_paths # :doc:
  91. @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_command.rb" ]
  92. end
  93. end
  94. end
  95. end

lib/rails/command/actions.rb

0.0% lines covered

42 relevant lines. 0 lines covered and 42 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Command
  4. module Actions
  5. # Change to the application's path if there is no <tt>config.ru</tt> file in current directory.
  6. # This allows us to run <tt>rails server</tt> from other directories, but still get
  7. # the main <tt>config.ru</tt> and properly set the <tt>tmp</tt> directory.
  8. def set_application_directory!
  9. Dir.chdir(File.expand_path("../..", APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
  10. end
  11. def require_application_and_environment!
  12. require_application!
  13. require_environment!
  14. end
  15. def require_application!
  16. require ENGINE_PATH if defined?(ENGINE_PATH)
  17. if defined?(APP_PATH)
  18. require APP_PATH
  19. end
  20. end
  21. def require_environment!
  22. if defined?(APP_PATH)
  23. Rails.application.require_environment!
  24. end
  25. end
  26. if defined?(ENGINE_PATH)
  27. def load_tasks
  28. Rake.application.init("rails")
  29. Rake.application.load_rakefile
  30. end
  31. def load_generators
  32. engine = ::Rails::Engine.find(ENGINE_ROOT)
  33. Rails::Generators.namespace = engine.railtie_namespace
  34. engine.load_generators
  35. end
  36. else
  37. def load_tasks
  38. Rails.application.load_tasks
  39. end
  40. def load_generators
  41. Rails.application.load_generators
  42. end
  43. end
  44. end
  45. end
  46. end

lib/rails/command/base.rb

0.0% lines covered

117 relevant lines. 0 lines covered and 117 lines missed.
    
  1. # frozen_string_literal: true
  2. require "thor"
  3. require "erb"
  4. require "active_support/core_ext/string/filters"
  5. require "active_support/core_ext/string/inflections"
  6. require "rails/command/actions"
  7. module Rails
  8. module Command
  9. class Base < Thor
  10. class Error < Thor::Error # :nodoc:
  11. end
  12. include Actions
  13. class << self
  14. def exit_on_failure? # :nodoc:
  15. false
  16. end
  17. # Returns true when the app is a Rails engine.
  18. def engine?
  19. defined?(ENGINE_ROOT)
  20. end
  21. # Tries to get the description from a USAGE file one folder above the command
  22. # root.
  23. def desc(usage = nil, description = nil, options = {})
  24. if usage
  25. super
  26. else
  27. @desc ||= ERB.new(File.read(usage_path)).result(binding) if usage_path
  28. end
  29. end
  30. # Convenience method to get the namespace from the class name. It's the
  31. # same as Thor default except that the Command at the end of the class
  32. # is removed.
  33. def namespace(name = nil)
  34. if name
  35. super
  36. else
  37. @namespace ||= super.chomp("_command").sub(/:command:/, ":")
  38. end
  39. end
  40. # Convenience method to hide this command from the available ones when
  41. # running rails command.
  42. def hide_command!
  43. Rails::Command.hidden_commands << self
  44. end
  45. def inherited(base) #:nodoc:
  46. super
  47. if base.name && !base.name.end_with?("Base")
  48. Rails::Command.subclasses << base
  49. end
  50. end
  51. def perform(command, args, config) # :nodoc:
  52. if Rails::Command::HELP_MAPPINGS.include?(args.first)
  53. command, args = "help", []
  54. end
  55. dispatch(command, args.dup, nil, config)
  56. end
  57. def printing_commands
  58. namespaced_commands
  59. end
  60. def executable
  61. "rails #{command_name}"
  62. end
  63. # Use Rails' default banner.
  64. def banner(*)
  65. "#{executable} #{arguments.map(&:usage).join(' ')} [options]".squish
  66. end
  67. # Sets the base_name taking into account the current class namespace.
  68. #
  69. # Rails::Command::TestCommand.base_name # => 'rails'
  70. def base_name
  71. @base_name ||= begin
  72. if base = name.to_s.split("::").first
  73. base.underscore
  74. end
  75. end
  76. end
  77. # Return command name without namespaces.
  78. #
  79. # Rails::Command::TestCommand.command_name # => 'test'
  80. def command_name
  81. @command_name ||= begin
  82. if command = name.to_s.split("::").last
  83. command.chomp!("Command")
  84. command.underscore
  85. end
  86. end
  87. end
  88. # Path to lookup a USAGE description in a file.
  89. def usage_path
  90. if default_command_root
  91. path = File.join(default_command_root, "USAGE")
  92. path if File.exist?(path)
  93. end
  94. end
  95. # Default file root to place extra files a command might need, placed
  96. # one folder above the command file.
  97. #
  98. # For a Rails::Command::TestCommand placed in <tt>rails/command/test_command.rb</tt>
  99. # would return <tt>rails/test</tt>.
  100. def default_command_root
  101. path = File.expand_path(relative_command_path, __dir__)
  102. path if File.exist?(path)
  103. end
  104. private
  105. # Allow the command method to be called perform.
  106. def create_command(meth)
  107. if meth == "perform"
  108. alias_method command_name, meth
  109. else
  110. # Prevent exception about command without usage.
  111. # Some commands define their documentation differently.
  112. @usage ||= ""
  113. @desc ||= ""
  114. super
  115. end
  116. end
  117. def command_root_namespace
  118. (namespace.split(":") - %w(rails)).join(":")
  119. end
  120. def relative_command_path
  121. File.join("../commands", *command_root_namespace.split(":"))
  122. end
  123. def namespaced_commands
  124. commands.keys.map do |key|
  125. if command_root_namespace.match?(/(\A|\:)#{key}\z/)
  126. command_root_namespace
  127. else
  128. "#{command_root_namespace}:#{key}"
  129. end
  130. end
  131. end
  132. end
  133. def help
  134. if command_name = self.class.command_name
  135. self.class.command_help(shell, command_name)
  136. else
  137. super
  138. end
  139. end
  140. end
  141. end
  142. end

lib/rails/command/behavior.rb

36.59% lines covered

41 relevant lines. 15 lines covered and 26 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 require "active_support"
  3. 16 module Rails
  4. 16 module Command
  5. 16 module Behavior #:nodoc:
  6. 16 extend ActiveSupport::Concern
  7. 16 class_methods do
  8. # Remove the color from output.
  9. 16 def no_color!
  10. 16 Thor::Base.shell = Thor::Shell::Basic
  11. end
  12. # Track all command subclasses.
  13. 16 def subclasses
  14. 23 @subclasses ||= []
  15. end
  16. 16 private
  17. # Prints a list of generators.
  18. 16 def print_list(base, namespaces)
  19. return if namespaces.empty?
  20. puts "#{base.camelize}:"
  21. namespaces.each do |namespace|
  22. puts(" #{namespace}")
  23. end
  24. puts
  25. end
  26. # Receives namespaces in an array and tries to find matching generators
  27. # in the load path.
  28. 16 def lookup(namespaces)
  29. paths = namespaces_to_paths(namespaces)
  30. paths.each do |raw_path|
  31. lookup_paths.each do |base|
  32. path = "#{base}/#{raw_path}_#{command_type}"
  33. begin
  34. require path
  35. return
  36. rescue LoadError => e
  37. raise unless /#{Regexp.escape(path)}$/.match?(e.message)
  38. rescue Exception => e
  39. warn "[WARNING] Could not load #{command_type} #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
  40. end
  41. end
  42. end
  43. end
  44. # This will try to load any command in the load path to show in help.
  45. 16 def lookup!
  46. $LOAD_PATH.each do |base|
  47. Dir[File.join(base, *file_lookup_paths)].each do |path|
  48. path = path.delete_prefix("#{base}/")
  49. require path
  50. rescue Exception
  51. # No problem
  52. end
  53. end
  54. end
  55. # Convert namespaces to paths by replacing ":" for "/" and adding
  56. # an extra lookup. For example, "rails:model" should be searched
  57. # in both: "rails/model/model_generator" and "rails/model_generator".
  58. 16 def namespaces_to_paths(namespaces)
  59. paths = []
  60. namespaces.each do |namespace|
  61. pieces = namespace.split(":")
  62. path = pieces.join("/")
  63. paths << "#{path}/#{pieces.last}"
  64. paths << path
  65. end
  66. paths.uniq!
  67. paths
  68. end
  69. end
  70. end
  71. end
  72. end

lib/rails/command/environment_argument.rb

0.0% lines covered

33 relevant lines. 0 lines covered and 33 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support"
  3. require "active_support/core_ext/class/attribute"
  4. module Rails
  5. module Command
  6. module EnvironmentArgument #:nodoc:
  7. extend ActiveSupport::Concern
  8. included do
  9. no_commands do
  10. class_attribute :environment_desc, default: "Specifies the environment to run this #{self.command_name} under (test/development/production)."
  11. end
  12. class_option :environment, aliases: "-e", type: :string, desc: environment_desc
  13. end
  14. private
  15. def extract_environment_option_from_argument(default_environment: Rails::Command.environment)
  16. if options[:environment]
  17. self.options = options.merge(environment: acceptable_environment(options[:environment]))
  18. else
  19. self.options = options.merge(environment: default_environment)
  20. end
  21. end
  22. def acceptable_environment(env = nil)
  23. if available_environments.include? env
  24. env
  25. else
  26. %w( production development test ).detect { |e| /^#{env}/.match?(e) } || env
  27. end
  28. end
  29. def available_environments
  30. Dir["config/environments/*.rb"].map { |fname| File.basename(fname, ".*") }
  31. end
  32. end
  33. end
  34. end

lib/rails/command/helpers/editor.rb

0.0% lines covered

30 relevant lines. 0 lines covered and 30 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/encrypted_file"
  3. module Rails
  4. module Command
  5. module Helpers
  6. module Editor
  7. private
  8. def ensure_editor_available(command:)
  9. if ENV["EDITOR"].to_s.empty?
  10. say "No $EDITOR to open file in. Assign one like this:"
  11. say ""
  12. say %(EDITOR="mate --wait" #{command})
  13. say ""
  14. say "For editors that fork and exit immediately, it's important to pass a wait flag,"
  15. say "otherwise the credentials will be saved immediately with no chance to edit."
  16. false
  17. else
  18. true
  19. end
  20. end
  21. def catch_editing_exceptions
  22. yield
  23. rescue Interrupt
  24. say "Aborted changing file: nothing saved."
  25. rescue ActiveSupport::EncryptedFile::MissingKeyError => error
  26. say error.message
  27. end
  28. end
  29. end
  30. end
  31. end

lib/rails/command/spellchecker.rb

0.0% lines covered

42 relevant lines. 0 lines covered and 42 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Command
  4. module Spellchecker # :nodoc:
  5. class << self
  6. def suggest(word, from:)
  7. if defined?(DidYouMean::SpellChecker)
  8. DidYouMean::SpellChecker.new(dictionary: from.map(&:to_s)).correct(word).first
  9. else
  10. from.sort_by { |w| levenshtein_distance(word, w) }.first
  11. end
  12. end
  13. private
  14. # This code is based directly on the Text gem implementation.
  15. # Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
  16. #
  17. # Returns a value representing the "cost" of transforming str1 into str2.
  18. def levenshtein_distance(str1, str2) # :doc:
  19. s = str1
  20. t = str2
  21. n = s.length
  22. m = t.length
  23. return m if 0 == n
  24. return n if 0 == m
  25. d = (0..m).to_a
  26. x = nil
  27. # avoid duplicating an enumerable object in the loop
  28. str2_codepoint_enumerable = str2.each_codepoint
  29. str1.each_codepoint.with_index do |char1, i|
  30. e = i + 1
  31. str2_codepoint_enumerable.with_index do |char2, j|
  32. cost = (char1 == char2) ? 0 : 1
  33. x = [
  34. d[j + 1] + 1, # insertion
  35. e + 1, # deletion
  36. d[j] + cost # substitution
  37. ].min
  38. d[j] = e
  39. e = x
  40. end
  41. d[m] = x
  42. end
  43. x
  44. end
  45. end
  46. end
  47. end
  48. end

lib/rails/commands.rb

0.0% lines covered

13 relevant lines. 0 lines covered and 13 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/command"
  3. aliases = {
  4. "g" => "generate",
  5. "d" => "destroy",
  6. "c" => "console",
  7. "s" => "server",
  8. "db" => "dbconsole",
  9. "r" => "runner",
  10. "t" => "test"
  11. }
  12. command = ARGV.shift
  13. command = aliases[command] || command
  14. Rails::Command.invoke command, ARGV

lib/rails/commands/application/application_command.rb

0.0% lines covered

23 relevant lines. 0 lines covered and 23 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators"
  3. require "rails/generators/rails/app/app_generator"
  4. module Rails
  5. module Generators
  6. class AppGenerator # :nodoc:
  7. # We want to exit on failure to be kind to other libraries
  8. # This is only when accessing via CLI
  9. def self.exit_on_failure?
  10. true
  11. end
  12. end
  13. end
  14. module Command
  15. class ApplicationCommand < Base # :nodoc:
  16. hide_command!
  17. def help
  18. perform # Punt help output to the generator.
  19. end
  20. def perform(*args)
  21. Rails::Generators::AppGenerator.start \
  22. Rails::Generators::ARGVScrubber.new(args).prepare!
  23. end
  24. end
  25. end
  26. end

lib/rails/commands/console/console_command.rb

0.0% lines covered

78 relevant lines. 0 lines covered and 78 lines missed.
    
  1. # frozen_string_literal: true
  2. require "irb"
  3. require "irb/completion"
  4. require "rails/command/environment_argument"
  5. module Rails
  6. class Console
  7. module BacktraceCleaner
  8. def filter_backtrace(bt)
  9. if result = super
  10. Rails.backtrace_cleaner.filter([result]).first
  11. end
  12. end
  13. end
  14. def self.start(*args)
  15. new(*args).start
  16. end
  17. attr_reader :options, :app, :console
  18. def initialize(app, options = {})
  19. @app = app
  20. @options = options
  21. app.sandbox = sandbox?
  22. if sandbox? && app.config.disable_sandbox
  23. puts "Error: Unable to start console in sandbox mode as sandbox mode is disabled (config.disable_sandbox is true)."
  24. exit 1
  25. end
  26. app.load_console
  27. @console = app.config.console || IRB
  28. if @console == IRB
  29. IRB::WorkSpace.prepend(BacktraceCleaner)
  30. end
  31. end
  32. def sandbox?
  33. options[:sandbox]
  34. end
  35. def environment
  36. options[:environment]
  37. end
  38. alias_method :environment?, :environment
  39. def set_environment!
  40. Rails.env = environment
  41. end
  42. def start
  43. set_environment! if environment?
  44. if sandbox?
  45. puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})"
  46. puts "Any modifications you make will be rolled back on exit"
  47. else
  48. puts "Loading #{Rails.env} environment (Rails #{Rails.version})"
  49. end
  50. if defined?(console::ExtendCommandBundle)
  51. console::ExtendCommandBundle.include(Rails::ConsoleMethods)
  52. end
  53. console.start
  54. end
  55. end
  56. module Command
  57. class ConsoleCommand < Base # :nodoc:
  58. include EnvironmentArgument
  59. class_option :sandbox, aliases: "-s", type: :boolean, default: false,
  60. desc: "Rollback database modifications on exit."
  61. def initialize(args = [], local_options = {}, config = {})
  62. console_options = []
  63. # For the same behavior as OptionParser, leave only options after "--" in ARGV.
  64. termination = local_options.find_index("--")
  65. if termination
  66. console_options = local_options[termination + 1..-1]
  67. local_options = local_options[0...termination]
  68. end
  69. ARGV.replace(console_options)
  70. super(args, local_options, config)
  71. end
  72. def perform
  73. extract_environment_option_from_argument
  74. # RAILS_ENV needs to be set before config/application is required.
  75. ENV["RAILS_ENV"] = options[:environment]
  76. require_application_and_environment!
  77. Rails::Console.start(Rails.application, options)
  78. end
  79. end
  80. end
  81. end

lib/rails/commands/credentials/credentials_command.rb

0.0% lines covered

106 relevant lines. 0 lines covered and 106 lines missed.
    
  1. # frozen_string_literal: true
  2. require "pathname"
  3. require "active_support"
  4. require "rails/command/helpers/editor"
  5. require "rails/command/environment_argument"
  6. module Rails
  7. module Command
  8. class CredentialsCommand < Rails::Command::Base # :nodoc:
  9. include Helpers::Editor
  10. include EnvironmentArgument
  11. require_relative "credentials_command/diffing"
  12. include Diffing
  13. self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
  14. no_commands do
  15. def help
  16. say "Usage:\n #{self.class.banner}"
  17. say ""
  18. say self.class.desc
  19. end
  20. end
  21. def edit
  22. extract_environment_option_from_argument(default_environment: nil)
  23. require_application!
  24. ensure_editor_available(command: "bin/rails credentials:edit") || (return)
  25. ensure_encryption_key_has_been_added if credentials.key.nil?
  26. ensure_credentials_have_been_added
  27. ensure_rails_credentials_driver_is_set
  28. catch_editing_exceptions do
  29. change_credentials_in_system_editor
  30. end
  31. say "File encrypted and saved."
  32. rescue ActiveSupport::MessageEncryptor::InvalidMessage
  33. say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
  34. end
  35. def show
  36. extract_environment_option_from_argument(default_environment: nil)
  37. require_application!
  38. say credentials.read.presence || missing_credentials_message
  39. end
  40. option :enroll, type: :boolean, default: false,
  41. desc: "Enrolls project in credential file diffing with `git diff`"
  42. def diff(content_path = nil)
  43. if @content_path = content_path
  44. extract_environment_option_from_argument(default_environment: extract_environment_from_path(content_path))
  45. require_application!
  46. say credentials.read.presence || credentials.content_path.read
  47. else
  48. require_application!
  49. enroll_project_in_credentials_diffing if options[:enroll]
  50. end
  51. rescue ActiveSupport::MessageEncryptor::InvalidMessage
  52. say credentials.content_path.read
  53. end
  54. private
  55. def credentials
  56. Rails.application.encrypted(content_path, key_path: key_path)
  57. end
  58. def ensure_encryption_key_has_been_added
  59. encryption_key_file_generator.add_key_file(key_path)
  60. encryption_key_file_generator.ignore_key_file(key_path)
  61. end
  62. def ensure_credentials_have_been_added
  63. if options[:environment]
  64. encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
  65. else
  66. credentials_generator.add_credentials_file_silently
  67. end
  68. end
  69. def change_credentials_in_system_editor
  70. credentials.change do |tmp_path|
  71. system("#{ENV["EDITOR"]} #{tmp_path}")
  72. end
  73. end
  74. def missing_credentials_message
  75. if credentials.key.nil?
  76. "Missing '#{key_path}' to decrypt credentials. See `rails credentials:help`"
  77. else
  78. "File '#{content_path}' does not exist. Use `rails credentials:edit` to change that."
  79. end
  80. end
  81. def content_path
  82. @content_path ||= options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
  83. end
  84. def key_path
  85. options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
  86. end
  87. def extract_environment_from_path(path)
  88. available_environments.find { |env| path.include? env } if path.end_with?(".yml.enc")
  89. end
  90. def encryption_key_file_generator
  91. require "rails/generators"
  92. require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
  93. Rails::Generators::EncryptionKeyFileGenerator.new
  94. end
  95. def encrypted_file_generator
  96. require "rails/generators"
  97. require "rails/generators/rails/encrypted_file/encrypted_file_generator"
  98. Rails::Generators::EncryptedFileGenerator.new
  99. end
  100. def credentials_generator
  101. require "rails/generators"
  102. require "rails/generators/rails/credentials/credentials_generator"
  103. Rails::Generators::CredentialsGenerator.new
  104. end
  105. end
  106. end
  107. end

lib/rails/commands/credentials/credentials_command/diffing.rb

0.0% lines covered

33 relevant lines. 0 lines covered and 33 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails::Command::CredentialsCommand::Diffing # :nodoc:
  3. def enroll_project_in_credentials_diffing
  4. if enrolled?
  5. true
  6. else
  7. gitattributes.write(<<~end_of_template, mode: "a")
  8. config/credentials/*.yml.enc diff=rails_credentials
  9. config/credentials.yml.enc diff=rails_credentials
  10. end_of_template
  11. say "Project successfully enrolled!"
  12. say "Rails ensures the rails_credentials diff driver is set when running `credentials:edit`. See `credentials:help` for more."
  13. end
  14. end
  15. def ensure_rails_credentials_driver_is_set
  16. set_driver if enrolled? && !driver_configured?
  17. end
  18. private
  19. def enrolled?
  20. gitattributes.read.match?(/config\/credentials(\/\*)?\.yml\.enc diff=rails_credentials/)
  21. rescue Errno::ENOENT
  22. false
  23. end
  24. def driver_configured?
  25. system "git config --get diff.rails_credentials.textconv", out: File::NULL
  26. end
  27. def set_driver
  28. puts "running"
  29. system "git config diff.rails_credentials.textconv 'bin/rails credentials:diff'"
  30. end
  31. def gitattributes
  32. Rails.root.join(".gitattributes")
  33. end
  34. end

lib/rails/commands/db/system/change/change_command.rb

0.0% lines covered

20 relevant lines. 0 lines covered and 20 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators"
  3. require "rails/generators/rails/db/system/change/change_generator"
  4. module Rails
  5. module Command
  6. module Db
  7. module System
  8. class ChangeCommand < Base # :nodoc:
  9. class_option :to, desc: "The database system to switch to."
  10. def initialize(positional_args, option_args, *)
  11. @argv = positional_args + option_args
  12. super
  13. end
  14. def perform
  15. Rails::Generators::Db::System::ChangeGenerator.start(@argv)
  16. end
  17. end
  18. end
  19. end
  20. end
  21. end

lib/rails/commands/dbconsole/dbconsole_command.rb

0.0% lines covered

147 relevant lines. 0 lines covered and 147 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/deprecation"
  3. require "active_support/core_ext/string/filters"
  4. require "rails/command/environment_argument"
  5. module Rails
  6. class DBConsole
  7. def self.start(*args)
  8. new(*args).start
  9. end
  10. def initialize(options = {})
  11. @options = options
  12. end
  13. def start
  14. ENV["RAILS_ENV"] ||= @options[:environment] || environment
  15. config = db_config.configuration_hash
  16. case db_config.adapter
  17. when /^(jdbc)?mysql/
  18. args = {
  19. host: "--host",
  20. port: "--port",
  21. socket: "--socket",
  22. username: "--user",
  23. encoding: "--default-character-set",
  24. sslca: "--ssl-ca",
  25. sslcert: "--ssl-cert",
  26. sslcapath: "--ssl-capath",
  27. sslcipher: "--ssl-cipher",
  28. sslkey: "--ssl-key"
  29. }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
  30. if config[:password] && @options[:include_password]
  31. args << "--password=#{config[:password]}"
  32. elsif config[:password] && !config[:password].to_s.empty?
  33. args << "-p"
  34. end
  35. args << db_config.database
  36. find_cmd_and_exec(["mysql", "mysql5"], *args)
  37. when /^postgres|^postgis/
  38. ENV["PGUSER"] = config[:username] if config[:username]
  39. ENV["PGHOST"] = config[:host] if config[:host]
  40. ENV["PGPORT"] = config[:port].to_s if config[:port]
  41. ENV["PGPASSWORD"] = config[:password].to_s if config[:password] && @options[:include_password]
  42. find_cmd_and_exec("psql", db_config.database)
  43. when "sqlite3"
  44. args = []
  45. args << "-#{@options[:mode]}" if @options[:mode]
  46. args << "-header" if @options[:header]
  47. args << File.expand_path(db_config.database, Rails.respond_to?(:root) ? Rails.root : nil)
  48. find_cmd_and_exec("sqlite3", *args)
  49. when "oracle", "oracle_enhanced"
  50. logon = ""
  51. if config[:username]
  52. logon = config[:username].dup
  53. logon << "/#{config[:password]}" if config[:password] && @options[:include_password]
  54. logon << "@#{db_config.database}" if db_config.database
  55. end
  56. find_cmd_and_exec("sqlplus", logon)
  57. when "sqlserver"
  58. args = []
  59. args += ["-D", "#{db_config.database}"] if db_config.database
  60. args += ["-U", "#{config[:username]}"] if config[:username]
  61. args += ["-P", "#{config[:password]}"] if config[:password]
  62. if config[:host]
  63. host_arg = +"#{config[:host]}"
  64. host_arg << ":#{config[:port]}" if config[:port]
  65. args += ["-S", host_arg]
  66. end
  67. find_cmd_and_exec("sqsh", *args)
  68. else
  69. abort "Unknown command-line client for #{db_config.database}."
  70. end
  71. end
  72. def config
  73. db_config.configuration_hash
  74. end
  75. deprecate config: "please use db_config.configuration_hash"
  76. def db_config
  77. return @db_config if defined?(@db_config)
  78. # We need to check whether the user passed the database the
  79. # first time around to show a consistent error message to people
  80. # relying on 2-level database configuration.
  81. @db_config = configurations.configs_for(env_name: environment, name: database)
  82. unless @db_config
  83. raise ActiveRecord::AdapterNotSpecified,
  84. "'#{database}' database is not configured for '#{environment}'. Available configuration: #{configurations.inspect}"
  85. end
  86. @db_config
  87. end
  88. def environment
  89. Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment
  90. end
  91. def database
  92. @options.fetch(:database, "primary")
  93. end
  94. private
  95. def configurations # :doc:
  96. require APP_PATH
  97. ActiveRecord::Base.configurations = Rails.application.config.database_configuration
  98. ActiveRecord::Base.configurations
  99. end
  100. def find_cmd_and_exec(commands, *args) # :doc:
  101. commands = Array(commands)
  102. dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
  103. unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
  104. commands = commands.map { |cmd| "#{cmd}#{ext}" }
  105. end
  106. full_path_command = nil
  107. found = commands.detect do |cmd|
  108. dirs_on_path.detect do |path|
  109. full_path_command = File.join(path, cmd)
  110. begin
  111. stat = File.stat(full_path_command)
  112. rescue SystemCallError
  113. else
  114. stat.file? && stat.executable?
  115. end
  116. end
  117. end
  118. if found
  119. exec full_path_command, *args
  120. else
  121. abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
  122. end
  123. end
  124. end
  125. module Command
  126. class DbconsoleCommand < Base # :nodoc:
  127. include EnvironmentArgument
  128. class_option :include_password, aliases: "-p", type: :boolean,
  129. desc: "Automatically provide the password from database.yml"
  130. class_option :mode, enum: %w( html list line column ), type: :string,
  131. desc: "Automatically put the sqlite3 database in the specified mode (html, list, line, column)."
  132. class_option :header, type: :boolean
  133. class_option :connection, aliases: "-c", type: :string,
  134. desc: "Specifies the connection to use."
  135. class_option :database, aliases: "--db", type: :string,
  136. desc: "Specifies the database to use."
  137. def perform
  138. extract_environment_option_from_argument
  139. # RAILS_ENV needs to be set before config/application is required.
  140. ENV["RAILS_ENV"] = options[:environment]
  141. if options["connection"]
  142. ActiveSupport::Deprecation.warn(<<-MSG.squish)
  143. `connection` option is deprecated and will be removed in Rails 6.1. Please use `database` option instead.
  144. MSG
  145. options["database"] = options["connection"]
  146. end
  147. require_application_and_environment!
  148. Rails::DBConsole.start(options)
  149. end
  150. end
  151. end
  152. end

lib/rails/commands/destroy/destroy_command.rb

0.0% lines covered

21 relevant lines. 0 lines covered and 21 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators"
  3. module Rails
  4. module Command
  5. class DestroyCommand < Base # :nodoc:
  6. no_commands do
  7. def help
  8. require_application_and_environment!
  9. load_generators
  10. Rails::Generators.help self.class.command_name
  11. end
  12. end
  13. def perform(*)
  14. generator = args.shift
  15. return help unless generator
  16. require_application_and_environment!
  17. load_generators
  18. Rails::Generators.invoke generator, args, behavior: :revoke, destination_root: Rails::Command.root
  19. end
  20. end
  21. end
  22. end

lib/rails/commands/dev/dev_command.rb

0.0% lines covered

15 relevant lines. 0 lines covered and 15 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/dev_caching"
  3. module Rails
  4. module Command
  5. class DevCommand < Base # :nodoc:
  6. no_commands do
  7. def help
  8. say "rails dev:cache # Toggle development mode caching on/off."
  9. end
  10. end
  11. def cache
  12. Rails::DevCaching.enable_by_file
  13. end
  14. end
  15. end
  16. end

lib/rails/commands/encrypted/encrypted_command.rb

0.0% lines covered

67 relevant lines. 0 lines covered and 67 lines missed.
    
  1. # frozen_string_literal: true
  2. require "pathname"
  3. require "active_support"
  4. require "rails/command/helpers/editor"
  5. module Rails
  6. module Command
  7. class EncryptedCommand < Rails::Command::Base # :nodoc:
  8. include Helpers::Editor
  9. class_option :key, aliases: "-k", type: :string,
  10. default: "config/master.key", desc: "The Rails.root relative path to the encryption key"
  11. no_commands do
  12. def help
  13. say "Usage:\n #{self.class.banner}"
  14. say ""
  15. say self.class.desc
  16. end
  17. end
  18. def edit(file_path)
  19. require_application!
  20. encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
  21. ensure_editor_available(command: "bin/rails encrypted:edit") || (return)
  22. ensure_encryption_key_has_been_added(options[:key]) if encrypted.key.nil?
  23. ensure_encrypted_file_has_been_added(file_path, options[:key])
  24. catch_editing_exceptions do
  25. change_encrypted_file_in_system_editor(file_path, options[:key])
  26. end
  27. say "File encrypted and saved."
  28. rescue ActiveSupport::MessageEncryptor::InvalidMessage
  29. say "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?"
  30. end
  31. def show(file_path)
  32. require_application!
  33. encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
  34. say encrypted.read.presence || missing_encrypted_message(key: encrypted.key, key_path: options[:key], file_path: file_path)
  35. end
  36. private
  37. def ensure_encryption_key_has_been_added(key_path)
  38. encryption_key_file_generator.add_key_file(key_path)
  39. encryption_key_file_generator.ignore_key_file(key_path)
  40. end
  41. def ensure_encrypted_file_has_been_added(file_path, key_path)
  42. encrypted_file_generator.add_encrypted_file_silently(file_path, key_path)
  43. end
  44. def change_encrypted_file_in_system_editor(file_path, key_path)
  45. Rails.application.encrypted(file_path, key_path: key_path).change do |tmp_path|
  46. system("#{ENV["EDITOR"]} #{tmp_path}")
  47. end
  48. end
  49. def encryption_key_file_generator
  50. require "rails/generators"
  51. require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
  52. Rails::Generators::EncryptionKeyFileGenerator.new
  53. end
  54. def encrypted_file_generator
  55. require "rails/generators"
  56. require "rails/generators/rails/encrypted_file/encrypted_file_generator"
  57. Rails::Generators::EncryptedFileGenerator.new
  58. end
  59. def missing_encrypted_message(key:, key_path:, file_path:)
  60. if key.nil?
  61. "Missing '#{key_path}' to decrypt data. See `bin/rails encrypted:help`"
  62. else
  63. "File '#{file_path}' does not exist. Use `bin/rails encrypted:edit #{file_path}` to change that."
  64. end
  65. end
  66. end
  67. end
  68. end

lib/rails/commands/generate/generate_command.rb

0.0% lines covered

22 relevant lines. 0 lines covered and 22 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators"
  3. module Rails
  4. module Command
  5. class GenerateCommand < Base # :nodoc:
  6. no_commands do
  7. def help
  8. require_application_and_environment!
  9. load_generators
  10. Rails::Generators.help self.class.command_name
  11. end
  12. end
  13. def perform(*)
  14. generator = args.shift
  15. return help unless generator
  16. require_application_and_environment!
  17. load_generators
  18. ARGV.replace(args) # set up ARGV for third-party libraries
  19. Rails::Generators.invoke generator, args, behavior: :invoke, destination_root: Rails::Command.root
  20. end
  21. end
  22. end
  23. end

lib/rails/commands/help/help_command.rb

0.0% lines covered

11 relevant lines. 0 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Command
  4. class HelpCommand < Base # :nodoc:
  5. hide_command!
  6. def help(*)
  7. say self.class.desc
  8. Rails::Command.print_commands
  9. end
  10. end
  11. end
  12. end

lib/rails/commands/initializers/initializers_command.rb

0.0% lines covered

17 relevant lines. 0 lines covered and 17 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/command/environment_argument"
  3. module Rails
  4. module Command
  5. class InitializersCommand < Base # :nodoc:
  6. include EnvironmentArgument
  7. desc "initializers", "Print out all defined initializers in the order they are invoked by Rails."
  8. def perform
  9. extract_environment_option_from_argument
  10. ENV["RAILS_ENV"] = options[:environment]
  11. require_application_and_environment!
  12. Rails.application.initializers.tsort_each do |initializer|
  13. say "#{initializer.context_class}.#{initializer.name}"
  14. end
  15. end
  16. end
  17. end
  18. end

lib/rails/commands/new/new_command.rb

0.0% lines covered

16 relevant lines. 0 lines covered and 16 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Command
  4. class NewCommand < Base # :nodoc:
  5. no_commands do
  6. def help
  7. Rails::Command.invoke :application, [ "--help" ]
  8. end
  9. end
  10. def perform(*)
  11. say "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
  12. say "Type 'rails' for help."
  13. exit 1
  14. end
  15. end
  16. end
  17. end

lib/rails/commands/notes/notes_command.rb

0.0% lines covered

29 relevant lines. 0 lines covered and 29 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/source_annotation_extractor"
  3. module Rails
  4. module Command
  5. class NotesCommand < Base # :nodoc:
  6. class_option :annotations, aliases: "-a", desc: "Filter by specific annotations, e.g. Foobar TODO", type: :array, default: Rails::SourceAnnotationExtractor::Annotation.tags
  7. def perform(*)
  8. require_application_and_environment!
  9. deprecation_warning
  10. display_annotations
  11. end
  12. private
  13. def display_annotations
  14. annotations = options[:annotations]
  15. tag = (annotations.length > 1)
  16. Rails::SourceAnnotationExtractor.enumerate annotations.join("|"), tag: tag, dirs: directories
  17. end
  18. def directories
  19. Rails::SourceAnnotationExtractor::Annotation.directories + source_annotation_directories
  20. end
  21. def deprecation_warning
  22. return if source_annotation_directories.empty?
  23. ActiveSupport::Deprecation.warn("`SOURCE_ANNOTATION_DIRECTORIES` is deprecated and will be removed in Rails 6.1. You can add default directories by using config.annotations.register_directories instead.")
  24. end
  25. def source_annotation_directories
  26. ENV["SOURCE_ANNOTATION_DIRECTORIES"].to_s.split(",")
  27. end
  28. end
  29. end
  30. end

lib/rails/commands/plugin/plugin_command.rb

0.0% lines covered

34 relevant lines. 0 lines covered and 34 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Command
  4. class PluginCommand < Base # :nodoc:
  5. hide_command!
  6. def help
  7. run_plugin_generator %w( --help )
  8. end
  9. def self.banner(*) # :nodoc:
  10. "#{executable} new [options]"
  11. end
  12. class_option :rc, type: :string, default: File.join("~", ".railsrc"),
  13. desc: "Initialize the plugin command with previous defaults. Uses .railsrc in your home directory by default."
  14. class_option :no_rc, desc: "Skip evaluating .railsrc."
  15. def perform(type = nil, *plugin_args)
  16. plugin_args << "--help" unless type == "new"
  17. unless options.key?("no_rc") # Thor's not so indifferent access hash.
  18. railsrc = File.expand_path(options[:rc])
  19. if File.exist?(railsrc)
  20. extra_args = File.read(railsrc).split(/\n+/).flat_map(&:split)
  21. say "Using #{extra_args.join(" ")} from #{railsrc}"
  22. plugin_args.insert(1, *extra_args)
  23. end
  24. end
  25. run_plugin_generator plugin_args
  26. end
  27. private
  28. def run_plugin_generator(plugin_args)
  29. require "rails/generators"
  30. require "rails/generators/rails/plugin/plugin_generator"
  31. Rails::Generators::PluginGenerator.start plugin_args
  32. end
  33. end
  34. end
  35. end

lib/rails/commands/rake/rake_command.rb

0.0% lines covered

41 relevant lines. 0 lines covered and 41 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Command
  4. class RakeCommand < Base # :nodoc:
  5. extend Rails::Command::Actions
  6. namespace "rake"
  7. class << self
  8. def printing_commands
  9. formatted_rake_tasks.map(&:first)
  10. end
  11. def perform(task, args, config)
  12. require_rake
  13. Rake.with_application do |rake|
  14. load "rails/tasks.rb"
  15. rake.init("rails", [task, *args])
  16. rake.load_rakefile
  17. if Rails.respond_to?(:root)
  18. rake.options.suppress_backtrace_pattern = /\A(?!#{Regexp.quote(Rails.root.to_s)})/
  19. end
  20. rake.standard_exception_handling { rake.top_level }
  21. end
  22. end
  23. private
  24. def rake_tasks
  25. require_rake
  26. return @rake_tasks if defined?(@rake_tasks)
  27. require_application!
  28. Rake::TaskManager.record_task_metadata = true
  29. Rake.application.instance_variable_set(:@name, "rails")
  30. load_tasks
  31. @rake_tasks = Rake.application.tasks.select(&:comment)
  32. end
  33. def formatted_rake_tasks
  34. rake_tasks.map { |t| [ t.name_with_args, t.comment ] }
  35. end
  36. def require_rake
  37. require "rake" # Defer booting Rake until we know it's needed.
  38. end
  39. end
  40. end
  41. end
  42. end

lib/rails/commands/routes/routes_command.rb

0.0% lines covered

29 relevant lines. 0 lines covered and 29 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/command"
  3. module Rails
  4. module Command
  5. class RoutesCommand < Base # :nodoc:
  6. class_option :controller, aliases: "-c", desc: "Filter by a specific controller, e.g. PostsController or Admin::PostsController."
  7. class_option :grep, aliases: "-g", desc: "Grep routes by a specific pattern."
  8. class_option :expanded, type: :boolean, aliases: "-E", desc: "Print routes expanded vertically with parts explained."
  9. def perform(*)
  10. require_application_and_environment!
  11. require "action_dispatch/routing/inspector"
  12. say inspector.format(formatter, routes_filter)
  13. end
  14. private
  15. def inspector
  16. ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
  17. end
  18. def formatter
  19. if options.key?("expanded")
  20. ActionDispatch::Routing::ConsoleFormatter::Expanded.new
  21. else
  22. ActionDispatch::Routing::ConsoleFormatter::Sheet.new
  23. end
  24. end
  25. def routes_filter
  26. options.symbolize_keys.slice(:controller, :grep)
  27. end
  28. end
  29. end
  30. end

lib/rails/commands/runner/runner_command.rb

0.0% lines covered

45 relevant lines. 0 lines covered and 45 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/command/environment_argument"
  3. module Rails
  4. module Command
  5. class RunnerCommand < Base # :nodoc:
  6. include EnvironmentArgument
  7. self.environment_desc = "The environment for the runner to operate under (test/development/production)"
  8. no_commands do
  9. def help
  10. super
  11. say self.class.desc
  12. end
  13. end
  14. def self.banner(*)
  15. "#{super} [<'Some.ruby(code)'> | <filename.rb> | -]"
  16. end
  17. def perform(code_or_file = nil, *command_argv)
  18. extract_environment_option_from_argument
  19. unless code_or_file
  20. help
  21. exit 1
  22. end
  23. ENV["RAILS_ENV"] = options[:environment]
  24. require_application_and_environment!
  25. Rails.application.load_runner
  26. ARGV.replace(command_argv)
  27. if code_or_file == "-"
  28. eval($stdin.read, TOPLEVEL_BINDING, "stdin")
  29. elsif File.exist?(code_or_file)
  30. $0 = code_or_file
  31. Kernel.load code_or_file
  32. else
  33. begin
  34. eval(code_or_file, TOPLEVEL_BINDING, __FILE__, __LINE__)
  35. rescue SyntaxError, NameError => e
  36. error "Please specify a valid ruby command or the path of a script to run."
  37. error "Run '#{self.class.executable} -h' for help."
  38. error ""
  39. error e
  40. exit 1
  41. end
  42. end
  43. end
  44. end
  45. end
  46. end

lib/rails/commands/secrets/secrets_command.rb

0.0% lines covered

53 relevant lines. 0 lines covered and 53 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support"
  3. require "rails/secrets"
  4. module Rails
  5. module Command
  6. class SecretsCommand < Rails::Command::Base # :nodoc:
  7. no_commands do
  8. def help
  9. say "Usage:\n #{self.class.banner}"
  10. say ""
  11. say self.class.desc
  12. end
  13. end
  14. def setup
  15. deprecate_in_favor_of_credentials_and_exit
  16. end
  17. def edit
  18. if ENV["EDITOR"].to_s.empty?
  19. say "No $EDITOR to open decrypted secrets in. Assign one like this:"
  20. say ""
  21. say %(EDITOR="mate --wait" rails secrets:edit)
  22. say ""
  23. say "For editors that fork and exit immediately, it's important to pass a wait flag,"
  24. say "otherwise the secrets will be saved immediately with no chance to edit."
  25. return
  26. end
  27. require_application_and_environment!
  28. Rails::Secrets.read_for_editing do |tmp_path|
  29. system("#{ENV["EDITOR"]} #{tmp_path}")
  30. end
  31. say "New secrets encrypted and saved."
  32. rescue Interrupt
  33. say "Aborted changing encrypted secrets: nothing saved."
  34. rescue Rails::Secrets::MissingKeyError => error
  35. say error.message
  36. rescue Errno::ENOENT => error
  37. if /secrets\.yml\.enc/.match?(error.message)
  38. deprecate_in_favor_of_credentials_and_exit
  39. else
  40. raise
  41. end
  42. end
  43. def show
  44. say Rails::Secrets.read
  45. end
  46. private
  47. def deprecate_in_favor_of_credentials_and_exit
  48. say "Encrypted secrets is deprecated in favor of credentials. Run:"
  49. say "rails credentials:help"
  50. exit 1
  51. end
  52. end
  53. end
  54. end

lib/rails/commands/server/server_command.rb

0.0% lines covered

254 relevant lines. 0 lines covered and 254 lines missed.
    
  1. # frozen_string_literal: true
  2. require "fileutils"
  3. require "action_dispatch"
  4. require "rails"
  5. require "active_support/deprecation"
  6. require "active_support/core_ext/string/filters"
  7. require "active_support/core_ext/symbol/starts_ends_with"
  8. require "rails/dev_caching"
  9. require "rails/command/environment_argument"
  10. module Rails
  11. class Server < ::Rack::Server
  12. class Options
  13. def parse!(args)
  14. Rails::Command::ServerCommand.new([], args).server_options
  15. end
  16. end
  17. def initialize(options = nil)
  18. @default_options = options || {}
  19. super(@default_options)
  20. set_environment
  21. end
  22. def opt_parser
  23. Options.new
  24. end
  25. def set_environment
  26. ENV["RAILS_ENV"] ||= options[:environment]
  27. end
  28. def start(after_stop_callback = nil)
  29. trap(:INT) { exit }
  30. create_tmp_directories
  31. setup_dev_caching
  32. log_to_stdout if options[:log_stdout]
  33. super()
  34. ensure
  35. after_stop_callback.call if after_stop_callback
  36. end
  37. def serveable? # :nodoc:
  38. server
  39. true
  40. rescue LoadError, NameError
  41. false
  42. end
  43. def middleware
  44. Hash.new([])
  45. end
  46. def default_options
  47. super.merge(@default_options)
  48. end
  49. def served_url
  50. "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma?
  51. end
  52. private
  53. def setup_dev_caching
  54. if options[:environment] == "development"
  55. Rails::DevCaching.enable_by_argument(options[:caching])
  56. end
  57. end
  58. def create_tmp_directories
  59. %w(cache pids sockets).each do |dir_to_make|
  60. FileUtils.mkdir_p(File.join(Rails.root, "tmp", dir_to_make))
  61. end
  62. end
  63. def log_to_stdout
  64. wrapped_app # touch the app so the logger is set up
  65. console = ActiveSupport::Logger.new(STDOUT)
  66. console.formatter = Rails.logger.formatter
  67. console.level = Rails.logger.level
  68. unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT)
  69. Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
  70. end
  71. end
  72. def use_puma?
  73. server.to_s == "Rack::Handler::Puma"
  74. end
  75. end
  76. module Command
  77. class ServerCommand < Base # :nodoc:
  78. include EnvironmentArgument
  79. # Hard-coding a bunch of handlers here as we don't have a public way of
  80. # querying them from the Rack::Handler registry.
  81. RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn falcon)
  82. DEFAULT_PORT = 3000
  83. DEFAULT_PIDFILE = "tmp/pids/server.pid"
  84. argument :using, optional: true
  85. class_option :port, aliases: "-p", type: :numeric,
  86. desc: "Runs Rails on the specified port - defaults to 3000.", banner: :port
  87. class_option :binding, aliases: "-b", type: :string,
  88. desc: "Binds Rails to the specified IP - defaults to 'localhost' in development and '0.0.0.0' in other environments'.",
  89. banner: :IP
  90. class_option :config, aliases: "-c", type: :string, default: "config.ru",
  91. desc: "Uses a custom rackup configuration.", banner: :file
  92. class_option :daemon, aliases: "-d", type: :boolean, default: false,
  93. desc: "Runs server as a Daemon."
  94. class_option :using, aliases: "-u", type: :string,
  95. desc: "Specifies the Rack server used to run the application (thin/puma/webrick).", banner: :name
  96. class_option :pid, aliases: "-P", type: :string,
  97. desc: "Specifies the PID file - defaults to #{DEFAULT_PIDFILE}."
  98. class_option :dev_caching, aliases: "-C", type: :boolean, default: nil,
  99. desc: "Specifies whether to perform caching in development."
  100. class_option :restart, type: :boolean, default: nil, hide: true
  101. class_option :early_hints, type: :boolean, default: nil, desc: "Enables HTTP/2 early hints."
  102. class_option :log_to_stdout, type: :boolean, default: nil, optional: true,
  103. desc: "Whether to log to stdout. Enabled by default in development when not daemonized."
  104. def initialize(args, local_options, *)
  105. super
  106. @original_options = local_options - %w( --restart )
  107. deprecate_positional_rack_server_and_rewrite_to_option(@original_options)
  108. end
  109. def perform
  110. extract_environment_option_from_argument
  111. set_application_directory!
  112. prepare_restart
  113. Rails::Server.new(server_options).tap do |server|
  114. # Require application after server sets environment to propagate
  115. # the --environment option.
  116. require APP_PATH
  117. Dir.chdir(Rails.application.root)
  118. if server.serveable?
  119. print_boot_information(server.server, server.served_url)
  120. after_stop_callback = -> { say "Exiting" unless options[:daemon] }
  121. server.start(after_stop_callback)
  122. else
  123. say rack_server_suggestion(using)
  124. end
  125. end
  126. end
  127. no_commands do
  128. def server_options
  129. {
  130. user_supplied_options: user_supplied_options,
  131. server: using,
  132. log_stdout: log_to_stdout?,
  133. Port: port,
  134. Host: host,
  135. DoNotReverseLookup: true,
  136. config: options[:config],
  137. environment: environment,
  138. daemonize: options[:daemon],
  139. pid: pid,
  140. caching: options[:dev_caching],
  141. restart_cmd: restart_command,
  142. early_hints: early_hints
  143. }
  144. end
  145. end
  146. private
  147. def user_supplied_options
  148. @user_supplied_options ||= begin
  149. # Convert incoming options array to a hash of flags
  150. # ["-p3001", "-C", "--binding", "127.0.0.1"] # => {"-p"=>true, "-C"=>true, "--binding"=>true}
  151. user_flag = {}
  152. @original_options.each do |command|
  153. if command.start_with?("--")
  154. option = command.split("=")[0]
  155. user_flag[option] = true
  156. elsif command =~ /\A(-.)/
  157. user_flag[Regexp.last_match[0]] = true
  158. end
  159. end
  160. # Collect all options that the user has explicitly defined so we can
  161. # differentiate them from defaults
  162. user_supplied_options = []
  163. self.class.class_options.select do |key, option|
  164. if option.aliases.any? { |name| user_flag[name] } || user_flag["--#{option.name}"]
  165. name = option.name.to_sym
  166. case name
  167. when :port
  168. name = :Port
  169. when :binding
  170. name = :Host
  171. when :dev_caching
  172. name = :caching
  173. when :daemonize
  174. name = :daemon
  175. end
  176. user_supplied_options << name
  177. end
  178. end
  179. user_supplied_options << :Host if ENV["HOST"] || ENV["BINDING"]
  180. user_supplied_options << :Port if ENV["PORT"]
  181. user_supplied_options << :pid if ENV["PIDFILE"]
  182. user_supplied_options.uniq
  183. end
  184. end
  185. def port
  186. options[:port] || ENV.fetch("PORT", DEFAULT_PORT).to_i
  187. end
  188. def host
  189. if options[:binding]
  190. options[:binding]
  191. else
  192. default_host = environment == "development" ? "localhost" : "0.0.0.0"
  193. if ENV["HOST"] && !ENV["BINDING"]
  194. ActiveSupport::Deprecation.warn(<<-MSG.squish)
  195. Using the `HOST` environment variable to specify the IP is deprecated and will be removed in Rails 6.1.
  196. Please use `BINDING` environment variable instead.
  197. MSG
  198. return ENV["HOST"]
  199. end
  200. ENV.fetch("BINDING", default_host)
  201. end
  202. end
  203. def environment
  204. options[:environment] || Rails::Command.environment
  205. end
  206. def restart_command
  207. "bin/rails server #{@original_options.join(" ")} --restart"
  208. end
  209. def early_hints
  210. options[:early_hints]
  211. end
  212. def log_to_stdout?
  213. options.fetch(:log_to_stdout) do
  214. options[:daemon].blank? && environment == "development"
  215. end
  216. end
  217. def pid
  218. File.expand_path(options[:pid] || ENV.fetch("PIDFILE", DEFAULT_PIDFILE))
  219. end
  220. def self.banner(*)
  221. "rails server -u [thin/puma/webrick] [options]"
  222. end
  223. def prepare_restart
  224. FileUtils.rm_f(pid) if options[:restart]
  225. end
  226. def deprecate_positional_rack_server_and_rewrite_to_option(original_options)
  227. if using
  228. ActiveSupport::Deprecation.warn(<<~MSG.squish)
  229. Passing the Rack server name as a regular argument is deprecated
  230. and will be removed in the next Rails version. Please, use the -u
  231. option instead.
  232. MSG
  233. original_options.concat [ "-u", using ]
  234. else
  235. # Use positional internally to get around Thor's immutable options.
  236. # TODO: Replace `using` occurrences with `options[:using]` after deprecation removal.
  237. @using = options[:using]
  238. end
  239. end
  240. def rack_server_suggestion(server)
  241. if server.in?(RACK_SERVERS)
  242. <<~MSG
  243. Could not load server "#{server}". Maybe you need to the add it to the Gemfile?
  244. gem "#{server}"
  245. Run `bin/rails server --help` for more options.
  246. MSG
  247. else
  248. suggestion = Rails::Command::Spellchecker.suggest(server, from: RACK_SERVERS)
  249. suggestion_msg = "Maybe you meant #{suggestion.inspect}?" if suggestion
  250. <<~MSG
  251. Could not find server "#{server}". #{suggestion_msg}
  252. Run `bin/rails server --help` for more options.
  253. MSG
  254. end
  255. end
  256. def print_boot_information(server, url)
  257. say <<~MSG
  258. => Booting #{ActiveSupport::Inflector.demodulize(server)}
  259. => Rails #{Rails.version} application starting in #{Rails.env} #{url}
  260. => Run `bin/rails server --help` for more startup options
  261. MSG
  262. end
  263. end
  264. end
  265. end

lib/rails/commands/version/version_command.rb

0.0% lines covered

9 relevant lines. 0 lines covered and 9 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Command
  4. class VersionCommand < Base # :nodoc:
  5. def perform
  6. Rails::Command.invoke :application, [ "--version" ]
  7. end
  8. end
  9. end
  10. end

lib/rails/configuration.rb

0.0% lines covered

104 relevant lines. 0 lines covered and 104 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/ordered_options"
  3. require "active_support/core_ext/object"
  4. require "rails/paths"
  5. require "rails/rack"
  6. module Rails
  7. module Configuration
  8. # MiddlewareStackProxy is a proxy for the Rails middleware stack that allows
  9. # you to configure middlewares in your application. It works basically as a
  10. # command recorder, saving each command to be applied after initialization
  11. # over the default middleware stack, so you can add, swap, or remove any
  12. # middleware in Rails.
  13. #
  14. # You can add your own middlewares by using the +config.middleware.use+ method:
  15. #
  16. # config.middleware.use Magical::Unicorns
  17. #
  18. # This will put the <tt>Magical::Unicorns</tt> middleware on the end of the stack.
  19. # You can use +insert_before+ if you wish to add a middleware before another:
  20. #
  21. # config.middleware.insert_before Rack::Head, Magical::Unicorns
  22. #
  23. # There's also +insert_after+ which will insert a middleware after another:
  24. #
  25. # config.middleware.insert_after Rack::Head, Magical::Unicorns
  26. #
  27. # Middlewares can also be completely swapped out and replaced with others:
  28. #
  29. # config.middleware.swap ActionDispatch::Flash, Magical::Unicorns
  30. #
  31. # Middlewares can be moved from one place to another:
  32. #
  33. # config.middleware.move_before ActionDispatch::Flash, Magical::Unicorns
  34. #
  35. # This will move the <tt>Magical::Unicorns</tt> middleware before the
  36. # <tt>ActionDispatch::Flash</tt>. You can also move it after:
  37. #
  38. # config.middleware.move_after ActionDispatch::Flash, Magical::Unicorns
  39. #
  40. # And finally they can also be removed from the stack completely:
  41. #
  42. # config.middleware.delete ActionDispatch::Flash
  43. #
  44. class MiddlewareStackProxy
  45. def initialize(operations = [], delete_operations = [])
  46. @operations = operations
  47. @delete_operations = delete_operations
  48. end
  49. def insert_before(*args, &block)
  50. @operations << -> middleware { middleware.send(__method__, *args, &block) }
  51. end
  52. ruby2_keywords(:insert_before) if respond_to?(:ruby2_keywords, true)
  53. alias :insert :insert_before
  54. def insert_after(*args, &block)
  55. @operations << -> middleware { middleware.send(__method__, *args, &block) }
  56. end
  57. ruby2_keywords(:insert_after) if respond_to?(:ruby2_keywords, true)
  58. def swap(*args, &block)
  59. @operations << -> middleware { middleware.send(__method__, *args, &block) }
  60. end
  61. ruby2_keywords(:swap) if respond_to?(:ruby2_keywords, true)
  62. def use(*args, &block)
  63. @operations << -> middleware { middleware.send(__method__, *args, &block) }
  64. end
  65. ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
  66. def delete(*args, &block)
  67. @delete_operations << -> middleware { middleware.send(__method__, *args, &block) }
  68. end
  69. def move_before(*args, &block)
  70. @delete_operations << -> middleware { middleware.send(__method__, *args, &block) }
  71. end
  72. alias :move :move_before
  73. def move_after(*args, &block)
  74. @delete_operations << -> middleware { middleware.send(__method__, *args, &block) }
  75. end
  76. def unshift(*args, &block)
  77. @operations << -> middleware { middleware.send(__method__, *args, &block) }
  78. end
  79. ruby2_keywords(:unshift) if respond_to?(:ruby2_keywords, true)
  80. def merge_into(other) #:nodoc:
  81. (@operations + @delete_operations).each do |operation|
  82. operation.call(other)
  83. end
  84. other
  85. end
  86. def +(other) # :nodoc:
  87. MiddlewareStackProxy.new(@operations + other.operations, @delete_operations + other.delete_operations)
  88. end
  89. protected
  90. attr_reader :operations, :delete_operations
  91. end
  92. class Generators #:nodoc:
  93. attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging, :api_only
  94. attr_reader :hidden_namespaces, :after_generate_callbacks
  95. def initialize
  96. @aliases = Hash.new { |h, k| h[k] = {} }
  97. @options = Hash.new { |h, k| h[k] = {} }
  98. @fallbacks = {}
  99. @templates = []
  100. @colorize_logging = true
  101. @api_only = false
  102. @hidden_namespaces = []
  103. @after_generate_callbacks = []
  104. end
  105. def initialize_copy(source)
  106. @aliases = @aliases.deep_dup
  107. @options = @options.deep_dup
  108. @fallbacks = @fallbacks.deep_dup
  109. @templates = @templates.dup
  110. end
  111. def hide_namespace(namespace)
  112. @hidden_namespaces << namespace
  113. end
  114. def after_generate(&block)
  115. @after_generate_callbacks << block
  116. end
  117. def method_missing(method, *args)
  118. method = method.to_s.delete_suffix("=").to_sym
  119. if args.empty?
  120. if method == :rails
  121. return @options[method]
  122. else
  123. return @options[:rails][method]
  124. end
  125. end
  126. if method == :rails || args.first.is_a?(Hash)
  127. namespace, configuration = method, args.shift
  128. else
  129. namespace, configuration = args.shift, args.shift
  130. namespace = namespace.to_sym if namespace.respond_to?(:to_sym)
  131. @options[:rails][method] = namespace
  132. end
  133. if configuration
  134. aliases = configuration.delete(:aliases)
  135. @aliases[namespace].merge!(aliases) if aliases
  136. @options[namespace].merge!(configuration)
  137. end
  138. end
  139. end
  140. end
  141. end

lib/rails/console/app.rb

0.0% lines covered

25 relevant lines. 0 lines covered and 25 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/all"
  3. require "action_controller"
  4. module Rails
  5. module ConsoleMethods
  6. # reference the global "app" instance, created on demand. To recreate the
  7. # instance, pass a non-false value as the parameter.
  8. def app(create = false)
  9. @app_integration_instance = nil if create
  10. @app_integration_instance ||= new_session do |sess|
  11. sess.host! "www.example.com"
  12. end
  13. end
  14. # create a new session. If a block is given, the new session will be yielded
  15. # to the block before being returned.
  16. def new_session
  17. app = Rails.application
  18. session = ActionDispatch::Integration::Session.new(app)
  19. yield session if block_given?
  20. # This makes app.url_for and app.foo_path available in the console
  21. session.extend(app.routes.url_helpers)
  22. session.extend(app.routes.mounted_helpers)
  23. session
  24. end
  25. # reloads the environment
  26. def reload!(print = true)
  27. puts "Reloading..." if print
  28. Rails.application.reloader.reload!
  29. true
  30. end
  31. end
  32. end

lib/rails/console/helpers.rb

0.0% lines covered

10 relevant lines. 0 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module ConsoleMethods
  4. # Gets the helper methods available to the controller.
  5. #
  6. # This method assumes an +ApplicationController+ exists, and it extends +ActionController::Base+
  7. def helper
  8. ApplicationController.helpers
  9. end
  10. # Gets a new instance of a controller object.
  11. #
  12. # This method assumes an +ApplicationController+ exists, and it extends +ActionController::Base+
  13. def controller
  14. @controller ||= ApplicationController.new
  15. end
  16. end
  17. end

lib/rails/dev_caching.rb

0.0% lines covered

34 relevant lines. 0 lines covered and 34 lines missed.
    
  1. # frozen_string_literal: true
  2. require "fileutils"
  3. module Rails
  4. module DevCaching # :nodoc:
  5. class << self
  6. FILE = "tmp/caching-dev.txt"
  7. def enable_by_file
  8. FileUtils.mkdir_p("tmp")
  9. if File.exist?(FILE)
  10. delete_cache_file
  11. puts "Development mode is no longer being cached."
  12. else
  13. create_cache_file
  14. puts "Development mode is now being cached."
  15. end
  16. FileUtils.touch "tmp/restart.txt"
  17. end
  18. def enable_by_argument(caching)
  19. FileUtils.mkdir_p("tmp")
  20. if caching
  21. create_cache_file
  22. elsif caching == false && File.exist?(FILE)
  23. delete_cache_file
  24. end
  25. end
  26. private
  27. def create_cache_file
  28. FileUtils.touch FILE
  29. end
  30. def delete_cache_file
  31. File.delete FILE
  32. end
  33. end
  34. end
  35. end

lib/rails/engine.rb

0.0% lines covered

294 relevant lines. 0 lines covered and 294 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/railtie"
  3. require "rails/engine/railties"
  4. require "active_support/core_ext/module/delegation"
  5. require "active_support/core_ext/object/try"
  6. require "pathname"
  7. require "thread"
  8. module Rails
  9. # <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
  10. # functionality and share it with other applications or within a larger packaged application.
  11. # Every <tt>Rails::Application</tt> is just an engine, which allows for simple
  12. # feature and application sharing.
  13. #
  14. # Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
  15. # methods (like <tt>rake_tasks</tt> and +generators+) and configuration
  16. # options that are available in railties can also be used in engines.
  17. #
  18. # == Creating an Engine
  19. #
  20. # If you want a gem to behave as an engine, you have to specify an +Engine+
  21. # for it somewhere inside your plugin's +lib+ folder (similar to how we
  22. # specify a +Railtie+):
  23. #
  24. # # lib/my_engine.rb
  25. # module MyEngine
  26. # class Engine < Rails::Engine
  27. # end
  28. # end
  29. #
  30. # Then ensure that this file is loaded at the top of your <tt>config/application.rb</tt>
  31. # (or in your +Gemfile+) and it will automatically load models, controllers and helpers
  32. # inside +app+, load routes at <tt>config/routes.rb</tt>, load locales at
  33. # <tt>config/locales/*</tt>, and load tasks at <tt>lib/tasks/*</tt>.
  34. #
  35. # == Configuration
  36. #
  37. # Like railties, engines can access a config object which contains configuration shared by
  38. # all railties and the application.
  39. # Additionally, each engine can access <tt>autoload_paths</tt>, <tt>eager_load_paths</tt> and
  40. # <tt>autoload_once_paths</tt> settings which are scoped to that engine.
  41. #
  42. # class MyEngine < Rails::Engine
  43. # # Add a load path for this specific Engine
  44. # config.autoload_paths << File.expand_path("lib/some/path", __dir__)
  45. #
  46. # initializer "my_engine.add_middleware" do |app|
  47. # app.middleware.use MyEngine::Middleware
  48. # end
  49. # end
  50. #
  51. # == Generators
  52. #
  53. # You can set up generators for engines with <tt>config.generators</tt> method:
  54. #
  55. # class MyEngine < Rails::Engine
  56. # config.generators do |g|
  57. # g.orm :active_record
  58. # g.template_engine :erb
  59. # g.test_framework :test_unit
  60. # end
  61. # end
  62. #
  63. # You can also set generators for an application by using <tt>config.app_generators</tt>:
  64. #
  65. # class MyEngine < Rails::Engine
  66. # # note that you can also pass block to app_generators in the same way you
  67. # # can pass it to generators method
  68. # config.app_generators.orm :datamapper
  69. # end
  70. #
  71. # == Paths
  72. #
  73. # Applications and engines have flexible path configuration, meaning that you
  74. # are not required to place your controllers at <tt>app/controllers</tt>, but
  75. # in any place which you find convenient.
  76. #
  77. # For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
  78. # You can set that as an option:
  79. #
  80. # class MyEngine < Rails::Engine
  81. # paths["app/controllers"] = "lib/controllers"
  82. # end
  83. #
  84. # You can also have your controllers loaded from both <tt>app/controllers</tt> and
  85. # <tt>lib/controllers</tt>:
  86. #
  87. # class MyEngine < Rails::Engine
  88. # paths["app/controllers"] << "lib/controllers"
  89. # end
  90. #
  91. # The available paths in an engine are:
  92. #
  93. # class MyEngine < Rails::Engine
  94. # paths["app"] # => ["app"]
  95. # paths["app/controllers"] # => ["app/controllers"]
  96. # paths["app/helpers"] # => ["app/helpers"]
  97. # paths["app/models"] # => ["app/models"]
  98. # paths["app/views"] # => ["app/views"]
  99. # paths["lib"] # => ["lib"]
  100. # paths["lib/tasks"] # => ["lib/tasks"]
  101. # paths["config"] # => ["config"]
  102. # paths["config/initializers"] # => ["config/initializers"]
  103. # paths["config/locales"] # => ["config/locales"]
  104. # paths["config/routes.rb"] # => ["config/routes.rb"]
  105. # end
  106. #
  107. # The <tt>Application</tt> class adds a couple more paths to this set. And as in your
  108. # <tt>Application</tt>, all folders under +app+ are automatically added to the load path.
  109. # If you have an <tt>app/services</tt> folder for example, it will be added by default.
  110. #
  111. # == Endpoint
  112. #
  113. # An engine can also be a Rack application. It can be useful if you have a Rack application that
  114. # you would like to provide with some of the +Engine+'s features.
  115. #
  116. # To do that, use the +endpoint+ method:
  117. #
  118. # module MyEngine
  119. # class Engine < Rails::Engine
  120. # endpoint MyRackApplication
  121. # end
  122. # end
  123. #
  124. # Now you can mount your engine in application's routes:
  125. #
  126. # Rails.application.routes.draw do
  127. # mount MyEngine::Engine => "/engine"
  128. # end
  129. #
  130. # == Middleware stack
  131. #
  132. # As an engine can now be a Rack endpoint, it can also have a middleware
  133. # stack. The usage is exactly the same as in <tt>Application</tt>:
  134. #
  135. # module MyEngine
  136. # class Engine < Rails::Engine
  137. # middleware.use SomeMiddleware
  138. # end
  139. # end
  140. #
  141. # == Routes
  142. #
  143. # If you don't specify an endpoint, routes will be used as the default
  144. # endpoint. You can use them just like you use an application's routes:
  145. #
  146. # # ENGINE/config/routes.rb
  147. # MyEngine::Engine.routes.draw do
  148. # get "/" => "posts#index"
  149. # end
  150. #
  151. # == Mount priority
  152. #
  153. # Note that now there can be more than one router in your application, and it's better to avoid
  154. # passing requests through many routers. Consider this situation:
  155. #
  156. # Rails.application.routes.draw do
  157. # mount MyEngine::Engine => "/blog"
  158. # get "/blog/omg" => "main#omg"
  159. # end
  160. #
  161. # +MyEngine+ is mounted at <tt>/blog</tt>, and <tt>/blog/omg</tt> points to application's
  162. # controller. In such a situation, requests to <tt>/blog/omg</tt> will go through +MyEngine+,
  163. # and if there is no such route in +Engine+'s routes, it will be dispatched to <tt>main#omg</tt>.
  164. # It's much better to swap that:
  165. #
  166. # Rails.application.routes.draw do
  167. # get "/blog/omg" => "main#omg"
  168. # mount MyEngine::Engine => "/blog"
  169. # end
  170. #
  171. # Now, +Engine+ will get only requests that were not handled by +Application+.
  172. #
  173. # == Engine name
  174. #
  175. # There are some places where an Engine's name is used:
  176. #
  177. # * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
  178. # it's used as default <tt>:as</tt> option
  179. # * rake task for installing migrations <tt>my_engine:install:migrations</tt>
  180. #
  181. # Engine name is set by default based on class name. For <tt>MyEngine::Engine</tt> it will be
  182. # <tt>my_engine_engine</tt>. You can change it manually using the <tt>engine_name</tt> method:
  183. #
  184. # module MyEngine
  185. # class Engine < Rails::Engine
  186. # engine_name "my_engine"
  187. # end
  188. # end
  189. #
  190. # == Isolated Engine
  191. #
  192. # Normally when you create controllers, helpers and models inside an engine, they are treated
  193. # as if they were created inside the application itself. This means that all helpers and
  194. # named routes from the application will be available to your engine's controllers as well.
  195. #
  196. # However, sometimes you want to isolate your engine from the application, especially if your engine
  197. # has its own router. To do that, you simply need to call +isolate_namespace+. This method requires
  198. # you to pass a module where all your controllers, helpers and models should be nested to:
  199. #
  200. # module MyEngine
  201. # class Engine < Rails::Engine
  202. # isolate_namespace MyEngine
  203. # end
  204. # end
  205. #
  206. # With such an engine, everything that is inside the +MyEngine+ module will be isolated from
  207. # the application.
  208. #
  209. # Consider this controller:
  210. #
  211. # module MyEngine
  212. # class FooController < ActionController::Base
  213. # end
  214. # end
  215. #
  216. # If the +MyEngine+ engine is marked as isolated, +FooController+ only has
  217. # access to helpers from +MyEngine+, and <tt>url_helpers</tt> from
  218. # <tt>MyEngine::Engine.routes</tt>.
  219. #
  220. # The next thing that changes in isolated engines is the behavior of routes.
  221. # Normally, when you namespace your controllers, you also need to namespace
  222. # the related routes. With an isolated engine, the engine's namespace is
  223. # automatically applied, so you don't need to specify it explicitly in your
  224. # routes:
  225. #
  226. # MyEngine::Engine.routes.draw do
  227. # resources :articles
  228. # end
  229. #
  230. # If +MyEngine+ is isolated, the routes above will point to
  231. # <tt>MyEngine::ArticlesController</tt>. You also don't need to use longer
  232. # URL helpers like +my_engine_articles_path+. Instead, you should simply use
  233. # +articles_path+, like you would do with your main application.
  234. #
  235. # To make this behavior consistent with other parts of the framework,
  236. # isolated engines also have an effect on <tt>ActiveModel::Naming</tt>. In a
  237. # normal Rails app, when you use a namespaced model such as
  238. # <tt>Namespace::Article</tt>, <tt>ActiveModel::Naming</tt> will generate
  239. # names with the prefix "namespace". In an isolated engine, the prefix will
  240. # be omitted in URL helpers and form fields, for convenience.
  241. #
  242. # polymorphic_url(MyEngine::Article.new)
  243. # # => "articles_path" # not "my_engine_articles_path"
  244. #
  245. # form_for(MyEngine::Article.new) do
  246. # text_field :title # => <input type="text" name="article[title]" id="article_title" />
  247. # end
  248. #
  249. # Additionally, an isolated engine will set its own name according to its
  250. # namespace, so <tt>MyEngine::Engine.engine_name</tt> will return
  251. # "my_engine". It will also set +MyEngine.table_name_prefix+ to "my_engine_",
  252. # meaning for example that <tt>MyEngine::Article</tt> will use the
  253. # +my_engine_articles+ database table by default.
  254. #
  255. # == Using Engine's routes outside Engine
  256. #
  257. # Since you can now mount an engine inside application's routes, you do not have direct access to +Engine+'s
  258. # <tt>url_helpers</tt> inside +Application+. When you mount an engine in an application's routes, a special helper is
  259. # created to allow you to do that. Consider such a scenario:
  260. #
  261. # # config/routes.rb
  262. # Rails.application.routes.draw do
  263. # mount MyEngine::Engine => "/my_engine", as: "my_engine"
  264. # get "/foo" => "foo#index"
  265. # end
  266. #
  267. # Now, you can use the <tt>my_engine</tt> helper inside your application:
  268. #
  269. # class FooController < ApplicationController
  270. # def index
  271. # my_engine.root_url # => /my_engine/
  272. # end
  273. # end
  274. #
  275. # There is also a <tt>main_app</tt> helper that gives you access to application's routes inside Engine:
  276. #
  277. # module MyEngine
  278. # class BarController
  279. # def index
  280. # main_app.foo_path # => /foo
  281. # end
  282. # end
  283. # end
  284. #
  285. # Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tt> as default, so most of the time
  286. # you can simply omit it.
  287. #
  288. # Finally, if you want to generate a URL to an engine's route using
  289. # <tt>polymorphic_url</tt>, you also need to pass the engine helper. Let's
  290. # say that you want to create a form pointing to one of the engine's routes.
  291. # All you need to do is pass the helper as the first element in array with
  292. # attributes for URL:
  293. #
  294. # form_for([my_engine, @user])
  295. #
  296. # This code will use <tt>my_engine.user_path(@user)</tt> to generate the proper route.
  297. #
  298. # == Isolated engine's helpers
  299. #
  300. # Sometimes you may want to isolate engine, but use helpers that are defined for it.
  301. # If you want to share just a few specific helpers you can add them to application's
  302. # helpers in ApplicationController:
  303. #
  304. # class ApplicationController < ActionController::Base
  305. # helper MyEngine::SharedEngineHelper
  306. # end
  307. #
  308. # If you want to include all of the engine's helpers, you can use the #helper method on an engine's
  309. # instance:
  310. #
  311. # class ApplicationController < ActionController::Base
  312. # helper MyEngine::Engine.helpers
  313. # end
  314. #
  315. # It will include all of the helpers from engine's directory. Take into account this does
  316. # not include helpers defined in controllers with helper_method or other similar solutions,
  317. # only helpers defined in the helpers directory will be included.
  318. #
  319. # == Migrations & seed data
  320. #
  321. # Engines can have their own migrations. The default path for migrations is exactly the same
  322. # as in application: <tt>db/migrate</tt>
  323. #
  324. # To use engine's migrations in application you can use the rake task below, which copies them to
  325. # application's dir:
  326. #
  327. # rake ENGINE_NAME:install:migrations
  328. #
  329. # Note that some of the migrations may be skipped if a migration with the same name already exists
  330. # in application. In such a situation you must decide whether to leave that migration or rename the
  331. # migration in the application and rerun copying migrations.
  332. #
  333. # If your engine has migrations, you may also want to prepare data for the database in
  334. # the <tt>db/seeds.rb</tt> file. You can load that data using the <tt>load_seed</tt> method, e.g.
  335. #
  336. # MyEngine::Engine.load_seed
  337. #
  338. # == Loading priority
  339. #
  340. # In order to change engine's priority you can use +config.railties_order+ in the main application.
  341. # It will affect the priority of loading views, helpers, assets, and all the other files
  342. # related to engine or application.
  343. #
  344. # # load Blog::Engine with highest priority, followed by application and other railties
  345. # config.railties_order = [Blog::Engine, :main_app, :all]
  346. class Engine < Railtie
  347. autoload :Configuration, "rails/engine/configuration"
  348. class << self
  349. attr_accessor :called_from, :isolated
  350. alias :isolated? :isolated
  351. alias :engine_name :railtie_name
  352. delegate :eager_load!, to: :instance
  353. def inherited(base)
  354. unless base.abstract_railtie?
  355. Rails::Railtie::Configuration.eager_load_namespaces << base
  356. base.called_from = begin
  357. call_stack = caller_locations.map { |l| l.absolute_path || l.path }
  358. File.dirname(call_stack.detect { |p| !p.match?(%r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack]) })
  359. end
  360. end
  361. super
  362. end
  363. def find_root(from)
  364. find_root_with_flag "lib", from
  365. end
  366. def endpoint(endpoint = nil)
  367. @endpoint ||= nil
  368. @endpoint = endpoint if endpoint
  369. @endpoint
  370. end
  371. def isolate_namespace(mod)
  372. engine_name(generate_railtie_name(mod.name))
  373. routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) }
  374. self.isolated = true
  375. unless mod.respond_to?(:railtie_namespace)
  376. name, railtie = engine_name, self
  377. mod.singleton_class.instance_eval do
  378. define_method(:railtie_namespace) { railtie }
  379. unless mod.respond_to?(:table_name_prefix)
  380. define_method(:table_name_prefix) { "#{name}_" }
  381. end
  382. unless mod.respond_to?(:use_relative_model_naming?)
  383. class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__
  384. end
  385. unless mod.respond_to?(:railtie_helpers_paths)
  386. define_method(:railtie_helpers_paths) { railtie.helpers_paths }
  387. end
  388. unless mod.respond_to?(:railtie_routes_url_helpers)
  389. define_method(:railtie_routes_url_helpers) { |include_path_helpers = true| railtie.routes.url_helpers(include_path_helpers) }
  390. end
  391. end
  392. end
  393. end
  394. # Finds engine with given path.
  395. def find(path)
  396. expanded_path = File.expand_path path
  397. Rails::Engine.subclasses.each do |klass|
  398. engine = klass.instance
  399. return engine if File.expand_path(engine.root) == expanded_path
  400. end
  401. nil
  402. end
  403. end
  404. delegate :middleware, :root, :paths, to: :config
  405. delegate :engine_name, :isolated?, to: :class
  406. def initialize
  407. @_all_autoload_paths = nil
  408. @_all_load_paths = nil
  409. @app = nil
  410. @config = nil
  411. @env_config = nil
  412. @helpers = nil
  413. @routes = nil
  414. @app_build_lock = Mutex.new
  415. super
  416. end
  417. # Load console and invoke the registered hooks.
  418. # Check <tt>Rails::Railtie.console</tt> for more info.
  419. def load_console(app = self)
  420. require "rails/console/app"
  421. require "rails/console/helpers"
  422. run_console_blocks(app)
  423. self
  424. end
  425. # Load Rails runner and invoke the registered hooks.
  426. # Check <tt>Rails::Railtie.runner</tt> for more info.
  427. def load_runner(app = self)
  428. run_runner_blocks(app)
  429. self
  430. end
  431. # Load Rake, railties tasks and invoke the registered hooks.
  432. # Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
  433. def load_tasks(app = self)
  434. require "rake"
  435. run_tasks_blocks(app)
  436. self
  437. end
  438. # Load Rails generators and invoke the registered hooks.
  439. # Check <tt>Rails::Railtie.generators</tt> for more info.
  440. def load_generators(app = self)
  441. require "rails/generators"
  442. run_generators_blocks(app)
  443. Rails::Generators.configure!(app.config.generators)
  444. self
  445. end
  446. def eager_load!
  447. # Already done by Zeitwerk::Loader.eager_load_all. We need this guard to
  448. # easily provide a compatible API for both zeitwerk and classic modes.
  449. return if Rails.autoloaders.zeitwerk_enabled?
  450. config.eager_load_paths.each do |load_path|
  451. # Starts after load_path plus a slash, ends before ".rb".
  452. relname_range = (load_path.to_s.length + 1)...-3
  453. Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
  454. require_dependency file[relname_range]
  455. end
  456. end
  457. end
  458. def railties
  459. @railties ||= Railties.new
  460. end
  461. # Returns a module with all the helpers defined for the engine.
  462. def helpers
  463. @helpers ||= begin
  464. helpers = Module.new
  465. all = ActionController::Base.all_helpers_from_path(helpers_paths)
  466. ActionController::Base.modules_for_helpers(all).each do |mod|
  467. helpers.include(mod)
  468. end
  469. helpers
  470. end
  471. end
  472. # Returns all registered helpers paths.
  473. def helpers_paths
  474. paths["app/helpers"].existent
  475. end
  476. # Returns the underlying Rack application for this engine.
  477. def app
  478. @app || @app_build_lock.synchronize {
  479. @app ||= begin
  480. stack = default_middleware_stack
  481. config.middleware = build_middleware.merge_into(stack)
  482. config.middleware.build(endpoint)
  483. end
  484. }
  485. end
  486. # Returns the endpoint for this engine. If none is registered,
  487. # defaults to an ActionDispatch::Routing::RouteSet.
  488. def endpoint
  489. self.class.endpoint || routes
  490. end
  491. # Define the Rack API for this engine.
  492. def call(env)
  493. req = build_request env
  494. app.call req.env
  495. end
  496. # Defines additional Rack env configuration that is added on each call.
  497. def env_config
  498. @env_config ||= {}
  499. end
  500. # Defines the routes for this engine. If a block is given to
  501. # routes, it is appended to the engine.
  502. def routes(&block)
  503. @routes ||= ActionDispatch::Routing::RouteSet.new_with_config(config)
  504. @routes.append(&block) if block_given?
  505. @routes
  506. end
  507. # Define the configuration object for the engine.
  508. def config
  509. @config ||= Engine::Configuration.new(self.class.find_root(self.class.called_from))
  510. end
  511. # Load data from db/seeds.rb file. It can be used in to load engines'
  512. # seeds, e.g.:
  513. #
  514. # Blog::Engine.load_seed
  515. def load_seed
  516. seed_file = paths["db/seeds.rb"].existent.first
  517. return unless seed_file
  518. if config.try(:active_job)&.queue_adapter == :async
  519. with_inline_jobs { load(seed_file) }
  520. else
  521. load(seed_file)
  522. end
  523. end
  524. initializer :load_environment_config, before: :load_environment_hook, group: :all do
  525. paths["config/environments"].existent.each do |environment|
  526. require environment
  527. end
  528. end
  529. initializer :set_load_path, before: :bootstrap_hook do |app|
  530. _all_load_paths(app.config.add_autoload_paths_to_load_path).reverse_each do |path|
  531. $LOAD_PATH.unshift(path) if File.directory?(path)
  532. end
  533. $LOAD_PATH.uniq!
  534. end
  535. # Set the paths from which Rails will automatically load source files,
  536. # and the load_once paths.
  537. #
  538. # This needs to be an initializer, since it needs to run once
  539. # per engine and get the engine as a block parameter.
  540. initializer :set_autoload_paths, before: :bootstrap_hook do
  541. ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
  542. ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
  543. config.autoload_paths.freeze
  544. config.autoload_once_paths.freeze
  545. end
  546. initializer :set_eager_load_paths, before: :bootstrap_hook do
  547. ActiveSupport::Dependencies._eager_load_paths.merge(config.eager_load_paths)
  548. config.eager_load_paths.freeze
  549. end
  550. initializer :add_routing_paths do |app|
  551. routing_paths = paths["config/routes.rb"].existent
  552. external_paths = self.paths["config/routes"].paths
  553. routes.draw_paths.concat(external_paths)
  554. if routes? || routing_paths.any?
  555. app.routes_reloader.paths.unshift(*routing_paths)
  556. app.routes_reloader.route_sets << routes
  557. app.routes_reloader.external_routes.unshift(*external_paths)
  558. end
  559. end
  560. # I18n load paths are a special case since the ones added
  561. # later have higher priority.
  562. initializer :add_locales do
  563. config.i18n.railties_load_path << paths["config/locales"]
  564. end
  565. initializer :add_view_paths do
  566. views = paths["app/views"].existent
  567. unless views.empty?
  568. ActiveSupport.on_load(:action_controller) { prepend_view_path(views) if respond_to?(:prepend_view_path) }
  569. ActiveSupport.on_load(:action_mailer) { prepend_view_path(views) }
  570. end
  571. end
  572. initializer :prepend_helpers_path do |app|
  573. if !isolated? || (app == self)
  574. app.config.helpers_paths.unshift(*paths["app/helpers"].existent)
  575. end
  576. end
  577. initializer :load_config_initializers do
  578. config.paths["config/initializers"].existent.sort.each do |initializer|
  579. load_config_initializer(initializer)
  580. end
  581. end
  582. initializer :engines_blank_point do
  583. # We need this initializer so all extra initializers added in engines are
  584. # consistently executed after all the initializers above across all engines.
  585. end
  586. rake_tasks do
  587. next if is_a?(Rails::Application)
  588. next unless has_migrations?
  589. namespace railtie_name do
  590. namespace :install do
  591. desc "Copy migrations from #{railtie_name} to application"
  592. task :migrations do
  593. ENV["FROM"] = railtie_name
  594. if Rake::Task.task_defined?("railties:install:migrations")
  595. Rake::Task["railties:install:migrations"].invoke
  596. else
  597. Rake::Task["app:railties:install:migrations"].invoke
  598. end
  599. end
  600. end
  601. end
  602. end
  603. def routes? #:nodoc:
  604. @routes
  605. end
  606. protected
  607. def run_tasks_blocks(*) #:nodoc:
  608. super
  609. paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
  610. end
  611. private
  612. def load_config_initializer(initializer) # :doc:
  613. ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do
  614. load(initializer)
  615. end
  616. end
  617. def with_inline_jobs
  618. queue_adapter = config.active_job.queue_adapter
  619. ActiveSupport.on_load(:active_job) do
  620. self.queue_adapter = :inline
  621. end
  622. yield
  623. ensure
  624. ActiveSupport.on_load(:active_job) do
  625. self.queue_adapter = queue_adapter
  626. end
  627. end
  628. def has_migrations?
  629. paths["db/migrate"].existent.any?
  630. end
  631. def self.find_root_with_flag(flag, root_path, default = nil) #:nodoc:
  632. while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
  633. parent = File.dirname(root_path)
  634. root_path = parent != root_path && parent
  635. end
  636. root = File.exist?("#{root_path}/#{flag}") ? root_path : default
  637. raise "Could not find root path for #{self}" unless root
  638. Pathname.new File.realpath root
  639. end
  640. def default_middleware_stack
  641. ActionDispatch::MiddlewareStack.new
  642. end
  643. def _all_autoload_once_paths
  644. config.autoload_once_paths
  645. end
  646. def _all_autoload_paths
  647. @_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
  648. end
  649. def _all_load_paths(add_autoload_paths_to_load_path)
  650. @_all_load_paths ||= begin
  651. load_paths = config.paths.load_paths
  652. load_paths += _all_autoload_paths if add_autoload_paths_to_load_path
  653. load_paths.uniq
  654. end
  655. end
  656. def build_request(env)
  657. env.merge!(env_config)
  658. req = ActionDispatch::Request.new env
  659. req.routes = routes
  660. req.engine_script_name = req.script_name
  661. req
  662. end
  663. def build_middleware
  664. config.middleware
  665. end
  666. end
  667. end

lib/rails/engine/commands.rb

0.0% lines covered

6 relevant lines. 0 lines covered and 6 lines missed.
    
  1. # frozen_string_literal: true
  2. unless defined?(APP_PATH)
  3. if File.exist?(File.expand_path("test/dummy/config/application.rb", ENGINE_ROOT))
  4. APP_PATH = File.expand_path("test/dummy/config/application", ENGINE_ROOT)
  5. end
  6. end
  7. require "rails/commands"

lib/rails/engine/configuration.rb

94.12% lines covered

51 relevant lines. 48 lines covered and 3 lines missed.
    
  1. # frozen_string_literal: true
  2. 17 require "rails/railtie/configuration"
  3. 17 module Rails
  4. 17 class Engine
  5. 17 class Configuration < ::Rails::Railtie::Configuration
  6. 17 attr_reader :root
  7. 17 attr_accessor :middleware, :javascript_path
  8. 17 attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths
  9. 17 def initialize(root = nil)
  10. 102 super()
  11. 102 @root = root
  12. 102 @generators = app_generators.dup
  13. 102 @middleware = Rails::Configuration::MiddlewareStackProxy.new
  14. 102 @javascript_path = "javascript"
  15. end
  16. # Holds generators configuration:
  17. #
  18. # config.generators do |g|
  19. # g.orm :data_mapper, migration: true
  20. # g.template_engine :haml
  21. # g.test_framework :rspec
  22. # end
  23. #
  24. # If you want to disable color in console, do:
  25. #
  26. # config.generators.colorize_logging = false
  27. #
  28. 17 def generators
  29. 32 @generators ||= Rails::Configuration::Generators.new
  30. 32 yield(@generators) if block_given?
  31. 32 @generators
  32. end
  33. 17 def paths
  34. 17 @paths ||= begin
  35. 17 paths = Rails::Paths::Root.new(@root)
  36. 17 paths.add "app", eager_load: true,
  37. glob: "{*,*/concerns}",
  38. exclude: ["assets", javascript_path]
  39. 17 paths.add "app/assets", glob: "*"
  40. 17 paths.add "app/controllers", eager_load: true
  41. 17 paths.add "app/channels", eager_load: true, glob: "**/*_channel.rb"
  42. 17 paths.add "app/helpers", eager_load: true
  43. 17 paths.add "app/models", eager_load: true
  44. 17 paths.add "app/mailers", eager_load: true
  45. 17 paths.add "app/views"
  46. 17 paths.add "lib", load_path: true
  47. 17 paths.add "lib/assets", glob: "*"
  48. 17 paths.add "lib/tasks", glob: "**/*.rake"
  49. 17 paths.add "config"
  50. 17 paths.add "config/environments", glob: "#{Rails.env}.rb"
  51. 17 paths.add "config/initializers", glob: "**/*.rb"
  52. 17 paths.add "config/locales", glob: "*.{rb,yml}"
  53. 17 paths.add "config/routes.rb"
  54. 17 paths.add "config/routes", glob: "**/*.rb"
  55. 17 paths.add "db"
  56. 17 paths.add "db/migrate"
  57. 17 paths.add "db/seeds.rb"
  58. 17 paths.add "vendor", load_path: true
  59. 17 paths.add "vendor/assets", glob: "*"
  60. 17 paths
  61. end
  62. end
  63. 17 def root=(value)
  64. 33 @root = paths.path = Pathname.new(value).expand_path
  65. end
  66. 17 def eager_load_paths
  67. @eager_load_paths ||= paths.eager_load
  68. end
  69. 17 def autoload_once_paths
  70. @autoload_once_paths ||= paths.autoload_once
  71. end
  72. 17 def autoload_paths
  73. @autoload_paths ||= paths.autoload_paths
  74. end
  75. end
  76. end
  77. end

lib/rails/engine/railties.rb

0.0% lines covered

18 relevant lines. 0 lines covered and 18 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. class Engine < Railtie
  4. class Railties
  5. include Enumerable
  6. attr_reader :_all
  7. def initialize
  8. @_all ||= ::Rails::Railtie.subclasses.map(&:instance) +
  9. ::Rails::Engine.subclasses.map(&:instance)
  10. end
  11. def each(*args, &block)
  12. _all.each(*args, &block)
  13. end
  14. def -(others)
  15. _all - others
  16. end
  17. end
  18. end
  19. end

lib/rails/engine/updater.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rails/generators"
  3. 1 require "rails/generators/rails/plugin/plugin_generator"
  4. 1 module Rails
  5. 1 class Engine
  6. 1 class Updater
  7. 1 class << self
  8. 1 def generator
  9. @generator ||= Rails::Generators::PluginGenerator.new ["plugin"],
  10. { engine: true }, { destination_root: ENGINE_ROOT }
  11. end
  12. 1 def run(action)
  13. generator.send(action)
  14. end
  15. end
  16. end
  17. end
  18. end

lib/rails/gem_version.rb

0.0% lines covered

12 relevant lines. 0 lines covered and 12 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. # Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
  4. def self.gem_version
  5. Gem::Version.new VERSION::STRING
  6. end
  7. module VERSION
  8. MAJOR = 6
  9. MINOR = 1
  10. TINY = 0
  11. PRE = "alpha"
  12. STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
  13. end
  14. end

lib/rails/generators.rb

47.65% lines covered

149 relevant lines. 71 lines covered and 78 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 activesupport_path = File.expand_path("../../../activesupport/lib", __dir__)
  3. 16 $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
  4. 16 require "thor/group"
  5. 16 require "rails/command"
  6. 16 require "active_support/core_ext/array/extract_options"
  7. 16 require "active_support/core_ext/enumerable"
  8. 16 require "active_support/core_ext/hash/deep_merge"
  9. 16 require "active_support/core_ext/module/attribute_accessors"
  10. 16 require "active_support/core_ext/string/indent"
  11. 16 require "active_support/core_ext/string/inflections"
  12. 16 module Rails
  13. 16 module Generators
  14. 16 include Rails::Command::Behavior
  15. 16 autoload :Actions, "rails/generators/actions"
  16. 16 autoload :ActiveModel, "rails/generators/active_model"
  17. 16 autoload :Base, "rails/generators/base"
  18. 16 autoload :Migration, "rails/generators/migration"
  19. 16 autoload :Database, "rails/generators/database"
  20. 16 autoload :AppName, "rails/generators/app_name"
  21. 16 autoload :NamedBase, "rails/generators/named_base"
  22. 16 autoload :ResourceHelpers, "rails/generators/resource_helpers"
  23. 16 autoload :TestCase, "rails/generators/test_case"
  24. 16 mattr_accessor :namespace
  25. 16 DEFAULT_ALIASES = {
  26. rails: {
  27. actions: "-a",
  28. orm: "-o",
  29. javascripts: "-j",
  30. javascript_engine: "-je",
  31. resource_controller: "-c",
  32. scaffold_controller: "-c",
  33. stylesheets: "-y",
  34. stylesheet_engine: "-se",
  35. scaffold_stylesheet: "-ss",
  36. template_engine: "-e",
  37. test_framework: "-t"
  38. },
  39. test_unit: {
  40. fixture_replacement: "-r",
  41. }
  42. }
  43. 16 DEFAULT_OPTIONS = {
  44. rails: {
  45. api: false,
  46. assets: true,
  47. force_plural: false,
  48. helper: true,
  49. integration_tool: nil,
  50. orm: false,
  51. resource_controller: :controller,
  52. resource_route: true,
  53. scaffold_controller: :scaffold_controller,
  54. stylesheets: true,
  55. stylesheet_engine: :css,
  56. scaffold_stylesheet: true,
  57. system_tests: nil,
  58. test_framework: nil,
  59. template_engine: :erb
  60. }
  61. }
  62. 16 class << self
  63. 16 def configure!(config) #:nodoc:
  64. 16 api_only! if config.api_only
  65. 16 no_color! unless config.colorize_logging
  66. 16 aliases.deep_merge! config.aliases
  67. 16 options.deep_merge! config.options
  68. 16 fallbacks.merge! config.fallbacks
  69. 16 templates_path.concat config.templates
  70. 16 templates_path.uniq!
  71. 16 hide_namespaces(*config.hidden_namespaces)
  72. 16 after_generate_callbacks.replace config.after_generate_callbacks
  73. end
  74. 16 def templates_path #:nodoc:
  75. 55 @templates_path ||= []
  76. end
  77. 16 def aliases #:nodoc:
  78. 204 @aliases ||= DEFAULT_ALIASES.dup
  79. end
  80. 16 def options #:nodoc:
  81. 292 @options ||= DEFAULT_OPTIONS.dup
  82. end
  83. 16 def after_generate_callbacks # :nodoc:
  84. 16 @after_generate_callbacks ||= []
  85. end
  86. # Hold configured generators fallbacks. If a plugin developer wants a
  87. # generator group to fallback to another group in case of missing generators,
  88. # they can add a fallback.
  89. #
  90. # For example, shoulda is considered a test_framework and is an extension
  91. # of test_unit. However, most part of shoulda generators are similar to
  92. # test_unit ones.
  93. #
  94. # Shoulda then can tell generators to search for test_unit generators when
  95. # some of them are not available by adding a fallback:
  96. #
  97. # Rails::Generators.fallbacks[:shoulda] = :test_unit
  98. 16 def fallbacks
  99. 16 @fallbacks ||= {}
  100. end
  101. # Configure generators for API only applications. It basically hides
  102. # everything that is usually browser related, such as assets and session
  103. # migration generators, and completely disable helpers and assets
  104. # so generators such as scaffold won't create them.
  105. 16 def api_only!
  106. hide_namespaces "assets", "helper", "css", "js"
  107. options[:rails].merge!(
  108. api: true,
  109. assets: false,
  110. helper: false,
  111. template_engine: nil
  112. )
  113. options[:mailer] ||= {}
  114. options[:mailer][:template_engine] ||= :erb
  115. end
  116. # Returns an array of generator namespaces that are hidden.
  117. # Generator namespaces may be hidden for a variety of reasons.
  118. # Some are aliased such as "rails:migration" and can be
  119. # invoked with the shorter "migration", others are private to other generators
  120. # such as "css:scaffold".
  121. 16 def hidden_namespaces
  122. 16 @hidden_namespaces ||= begin
  123. 16 orm = options[:rails][:orm]
  124. 16 test = options[:rails][:test_framework]
  125. 16 template = options[:rails][:template_engine]
  126. 16 css = options[:rails][:stylesheet_engine]
  127. 16 [
  128. "rails",
  129. "resource_route",
  130. "#{orm}:migration",
  131. "#{orm}:model",
  132. "#{test}:controller",
  133. "#{test}:helper",
  134. "#{test}:integration",
  135. "#{test}:system",
  136. "#{test}:mailer",
  137. "#{test}:model",
  138. "#{test}:scaffold",
  139. "#{test}:view",
  140. "#{test}:job",
  141. "#{template}:controller",
  142. "#{template}:scaffold",
  143. "#{template}:mailer",
  144. "#{css}:scaffold",
  145. "#{css}:assets",
  146. "css:assets",
  147. "css:scaffold",
  148. "action_text:install",
  149. "action_mailbox:install"
  150. ]
  151. end
  152. end
  153. 16 def hide_namespaces(*namespaces)
  154. 16 hidden_namespaces.concat(namespaces)
  155. end
  156. 16 alias hide_namespace hide_namespaces
  157. # Show help message with available generators.
  158. 16 def help(command = "generate")
  159. puts "Usage: rails #{command} GENERATOR [args] [options]"
  160. puts
  161. puts "General options:"
  162. puts " -h, [--help] # Print generator's options and usage"
  163. puts " -p, [--pretend] # Run but do not make any changes"
  164. puts " -f, [--force] # Overwrite files that already exist"
  165. puts " -s, [--skip] # Skip files that already exist"
  166. puts " -q, [--quiet] # Suppress status output"
  167. puts
  168. puts "Please choose a generator below."
  169. puts
  170. print_generators
  171. end
  172. 16 def public_namespaces
  173. lookup!
  174. subclasses.map(&:namespace)
  175. end
  176. 16 def print_generators
  177. sorted_groups.each { |b, n| print_list(b, n) }
  178. end
  179. 16 def sorted_groups
  180. namespaces = public_namespaces
  181. namespaces.sort!
  182. groups = Hash.new { |h, k| h[k] = [] }
  183. namespaces.each do |namespace|
  184. base = namespace.split(":").first
  185. groups[base] << namespace
  186. end
  187. rails = groups.delete("rails")
  188. rails.map! { |n| n.delete_prefix("rails:") }
  189. rails.delete("app")
  190. rails.delete("plugin")
  191. rails.delete("encrypted_secrets")
  192. rails.delete("encrypted_file")
  193. rails.delete("encryption_key_file")
  194. rails.delete("master_key")
  195. rails.delete("credentials")
  196. rails.delete("db:system:change")
  197. hidden_namespaces.each { |n| groups.delete(n.to_s) }
  198. [[ "rails", rails ]] + groups.sort.to_a
  199. end
  200. # Rails finds namespaces similar to Thor, it only adds one rule:
  201. #
  202. # Generators names must end with "_generator.rb". This is required because Rails
  203. # looks in load paths and loads the generator just before it's going to be used.
  204. #
  205. # find_by_namespace :webrat, :rails, :integration
  206. #
  207. # Will search for the following generators:
  208. #
  209. # "rails:webrat", "webrat:integration", "webrat"
  210. #
  211. # Notice that "rails:generators:webrat" could be loaded as well, what
  212. # Rails looks for is the first and last parts of the namespace.
  213. 16 def find_by_namespace(name, base = nil, context = nil) #:nodoc:
  214. lookups = []
  215. lookups << "#{base}:#{name}" if base
  216. lookups << "#{name}:#{context}" if context
  217. unless base || context
  218. unless name.to_s.include?(?:)
  219. lookups << "#{name}:#{name}"
  220. lookups << "rails:#{name}"
  221. end
  222. lookups << "#{name}"
  223. end
  224. lookup(lookups)
  225. namespaces = subclasses.index_by(&:namespace)
  226. lookups.each do |namespace|
  227. klass = namespaces[namespace]
  228. return klass if klass
  229. end
  230. invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
  231. end
  232. # Receives a namespace, arguments and the behavior to invoke the generator.
  233. # It's used as the default entry point for generate, destroy and update
  234. # commands.
  235. 16 def invoke(namespace, args = ARGV, config = {})
  236. names = namespace.to_s.split(":")
  237. if klass = find_by_namespace(names.pop, names.any? && names.join(":"))
  238. args << "--help" if args.empty? && klass.arguments.any?(&:required?)
  239. klass.start(args, config)
  240. run_after_generate_callback if config[:behavior] == :invoke
  241. else
  242. options = sorted_groups.flat_map(&:last)
  243. suggestion = Rails::Command::Spellchecker.suggest(namespace.to_s, from: options)
  244. suggestion_msg = "Maybe you meant #{suggestion.inspect}?" if suggestion
  245. puts <<~MSG
  246. Could not find generator '#{namespace}'. #{suggestion_msg}
  247. Run `bin/rails generate --help` for more options.
  248. MSG
  249. end
  250. end
  251. 16 def add_generated_file(file) # :nodoc:
  252. (@@generated_files ||= []) << file
  253. file
  254. end
  255. 16 private
  256. 16 def print_list(base, namespaces) # :doc:
  257. namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
  258. super
  259. end
  260. # Try fallbacks for the given base.
  261. 16 def invoke_fallbacks_for(name, base)
  262. return nil unless base && fallbacks[base.to_sym]
  263. invoked_fallbacks = []
  264. Array(fallbacks[base.to_sym]).each do |fallback|
  265. next if invoked_fallbacks.include?(fallback)
  266. invoked_fallbacks << fallback
  267. klass = find_by_namespace(name, fallback)
  268. return klass if klass
  269. end
  270. nil
  271. end
  272. 16 def command_type # :doc:
  273. @command_type ||= "generator"
  274. end
  275. 16 def lookup_paths # :doc:
  276. @lookup_paths ||= %w( rails/generators generators )
  277. end
  278. 16 def file_lookup_paths # :doc:
  279. @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ]
  280. end
  281. 16 def run_after_generate_callback
  282. if defined?(@@generated_files) && !@@generated_files.empty?
  283. @after_generate_callbacks.each do |callback|
  284. callback.call(@@generated_files)
  285. end
  286. @@generated_files = []
  287. end
  288. end
  289. end
  290. end
  291. end

lib/rails/generators/actions.rb

22.07% lines covered

145 relevant lines. 32 lines covered and 113 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 require "shellwords"
  3. 16 require "active_support/core_ext/kernel/reporting"
  4. 16 require "active_support/core_ext/string/strip"
  5. 16 module Rails
  6. 16 module Generators
  7. 16 module Actions
  8. 16 def initialize(*) # :nodoc:
  9. super
  10. @indentation = 0
  11. end
  12. # Adds an entry into +Gemfile+ for the supplied gem.
  13. #
  14. # gem "rspec", group: :test
  15. # gem "technoweenie-restful-authentication", lib: "restful-authentication", source: "http://gems.github.com/"
  16. # gem "rails", "3.0", git: "https://github.com/rails/rails"
  17. # gem "RedCloth", ">= 4.1.0", "< 4.2.0"
  18. 16 def gem(*args)
  19. options = args.extract_options!
  20. name, *versions = args
  21. # Set the message to be shown in logs. Uses the git repo if one is given,
  22. # otherwise use name (version).
  23. parts, message = [ quote(name) ], name.dup
  24. if versions = versions.any? ? versions : options.delete(:version)
  25. _versions = Array(versions)
  26. _versions.each do |version|
  27. parts << quote(version)
  28. end
  29. message << " (#{_versions.join(", ")})"
  30. end
  31. message = options[:git] if options[:git]
  32. log :gemfile, message
  33. parts << quote(options) unless options.empty?
  34. in_root do
  35. str = "gem #{parts.join(", ")}"
  36. str = indentation + str
  37. append_file_with_newline "Gemfile", str, verbose: false
  38. end
  39. end
  40. # Wraps gem entries inside a group.
  41. #
  42. # gem_group :development, :test do
  43. # gem "rspec-rails"
  44. # end
  45. 16 def gem_group(*names, &block)
  46. options = names.extract_options!
  47. str = names.map(&:inspect)
  48. str << quote(options) unless options.empty?
  49. str = str.join(", ")
  50. log :gemfile, "group #{str}"
  51. in_root do
  52. append_file_with_newline "Gemfile", "\ngroup #{str} do", force: true
  53. with_indentation(&block)
  54. append_file_with_newline "Gemfile", "end", force: true
  55. end
  56. end
  57. 16 def github(repo, options = {}, &block)
  58. str = [quote(repo)]
  59. str << quote(options) unless options.empty?
  60. str = str.join(", ")
  61. log :github, "github #{str}"
  62. in_root do
  63. if @indentation.zero?
  64. append_file_with_newline "Gemfile", "\ngithub #{str} do", force: true
  65. else
  66. append_file_with_newline "Gemfile", "#{indentation}github #{str} do", force: true
  67. end
  68. with_indentation(&block)
  69. append_file_with_newline "Gemfile", "#{indentation}end", force: true
  70. end
  71. end
  72. # Add the given source to +Gemfile+
  73. #
  74. # If block is given, gem entries in block are wrapped into the source group.
  75. #
  76. # add_source "http://gems.github.com/"
  77. #
  78. # add_source "http://gems.github.com/" do
  79. # gem "rspec-rails"
  80. # end
  81. 16 def add_source(source, options = {}, &block)
  82. log :source, source
  83. in_root do
  84. if block
  85. append_file_with_newline "Gemfile", "\nsource #{quote(source)} do", force: true
  86. with_indentation(&block)
  87. append_file_with_newline "Gemfile", "end", force: true
  88. else
  89. prepend_file "Gemfile", "source #{quote(source)}\n", verbose: false
  90. end
  91. end
  92. end
  93. # Adds a line inside the Application class for <tt>config/application.rb</tt>.
  94. #
  95. # If options <tt>:env</tt> is specified, the line is appended to the corresponding
  96. # file in <tt>config/environments</tt>.
  97. #
  98. # environment do
  99. # "config.action_controller.asset_host = 'cdn.provider.com'"
  100. # end
  101. #
  102. # environment(nil, env: "development") do
  103. # "config.action_controller.asset_host = 'localhost:3000'"
  104. # end
  105. 16 def environment(data = nil, options = {})
  106. sentinel = "class Application < Rails::Application\n"
  107. env_file_sentinel = "Rails.application.configure do\n"
  108. data ||= yield if block_given?
  109. in_root do
  110. if options[:env].nil?
  111. inject_into_file "config/application.rb", optimize_indentation(data, 4), after: sentinel, verbose: false
  112. else
  113. Array(options[:env]).each do |env|
  114. inject_into_file "config/environments/#{env}.rb", optimize_indentation(data, 2), after: env_file_sentinel, verbose: false
  115. end
  116. end
  117. end
  118. end
  119. 16 alias :application :environment
  120. # Run a command in git.
  121. #
  122. # git :init
  123. # git add: "this.file that.rb"
  124. # git add: "onefile.rb", rm: "badfile.cxx"
  125. 16 def git(commands = {})
  126. if commands.is_a?(Symbol)
  127. run "git #{commands}"
  128. else
  129. commands.each do |cmd, options|
  130. run "git #{cmd} #{options}"
  131. end
  132. end
  133. end
  134. # Create a new file in the <tt>vendor/</tt> directory. Code can be specified
  135. # in a block or a data string can be given.
  136. #
  137. # vendor("sekrit.rb") do
  138. # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--"
  139. # "salt = '#{sekrit_salt}'"
  140. # end
  141. #
  142. # vendor("foreign.rb", "# Foreign code is fun")
  143. 16 def vendor(filename, data = nil)
  144. log :vendor, filename
  145. data ||= yield if block_given?
  146. create_file("vendor/#{filename}", optimize_indentation(data), verbose: false)
  147. end
  148. # Create a new file in the <tt>lib/</tt> directory. Code can be specified
  149. # in a block or a data string can be given.
  150. #
  151. # lib("crypto.rb") do
  152. # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
  153. # end
  154. #
  155. # lib("foreign.rb", "# Foreign code is fun")
  156. 16 def lib(filename, data = nil)
  157. log :lib, filename
  158. data ||= yield if block_given?
  159. create_file("lib/#{filename}", optimize_indentation(data), verbose: false)
  160. end
  161. # Create a new +Rakefile+ with the provided code (either in a block or a string).
  162. #
  163. # rakefile("bootstrap.rake") do
  164. # project = ask("What is the UNIX name of your project?")
  165. #
  166. # <<-TASK
  167. # namespace :#{project} do
  168. # task :bootstrap do
  169. # puts "I like boots!"
  170. # end
  171. # end
  172. # TASK
  173. # end
  174. #
  175. # rakefile('seed.rake', 'puts "Planting seeds"')
  176. 16 def rakefile(filename, data = nil)
  177. log :rakefile, filename
  178. data ||= yield if block_given?
  179. create_file("lib/tasks/#{filename}", optimize_indentation(data), verbose: false)
  180. end
  181. # Create a new initializer with the provided code (either in a block or a string).
  182. #
  183. # initializer("globals.rb") do
  184. # data = ""
  185. #
  186. # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do |const|
  187. # data << "#{const} = :entp\n"
  188. # end
  189. #
  190. # data
  191. # end
  192. #
  193. # initializer("api.rb", "API_KEY = '123456'")
  194. 16 def initializer(filename, data = nil)
  195. log :initializer, filename
  196. data ||= yield if block_given?
  197. create_file("config/initializers/#{filename}", optimize_indentation(data), verbose: false)
  198. end
  199. # Generate something using a generator from Rails or a plugin.
  200. # The second parameter is the argument string that is passed to
  201. # the generator or an Array that is joined.
  202. #
  203. # generate(:authenticated, "user session")
  204. 16 def generate(what, *args)
  205. log :generate, what
  206. options = args.extract_options!
  207. options[:abort_on_failure] = !options[:inline]
  208. rails_command "generate #{what} #{args.join(" ")}", options
  209. end
  210. # Runs the supplied rake task (invoked with 'rake ...')
  211. #
  212. # rake("db:migrate")
  213. # rake("db:migrate", env: "production")
  214. # rake("gems:install", sudo: true)
  215. # rake("gems:install", capture: true)
  216. 16 def rake(command, options = {})
  217. execute_command :rake, command, options
  218. end
  219. # Runs the supplied rake task (invoked with 'rails ...')
  220. #
  221. # rails_command("db:migrate")
  222. # rails_command("db:migrate", env: "production")
  223. # rails_command("gems:install", sudo: true)
  224. # rails_command("gems:install", capture: true)
  225. 16 def rails_command(command, options = {})
  226. if options[:inline]
  227. log :rails, command
  228. command, *args = Shellwords.split(command)
  229. in_root do
  230. silence_warnings do
  231. ::Rails::Command.invoke(command, args, **options)
  232. end
  233. end
  234. else
  235. execute_command :rails, command, options
  236. end
  237. end
  238. # Make an entry in Rails routing file <tt>config/routes.rb</tt>
  239. #
  240. # route "root 'welcome#index'"
  241. # route "root 'admin#index'", namespace: :admin
  242. 16 def route(routing_code, namespace: nil)
  243. routing_code = Array(namespace).reverse.reduce(routing_code) do |code, ns|
  244. "namespace :#{ns} do\n#{indent(code, 2)}\nend"
  245. end
  246. log :route, routing_code
  247. sentinel = /\.routes\.draw do\s*\n/m
  248. in_root do
  249. inject_into_file "config/routes.rb", optimize_indentation(routing_code, 2), after: sentinel, verbose: false, force: false
  250. end
  251. end
  252. # Reads the given file at the source root and prints it in the console.
  253. #
  254. # readme "README"
  255. 16 def readme(path)
  256. log File.read(find_in_source_paths(path))
  257. end
  258. 16 private
  259. # Define log for backwards compatibility. If just one argument is sent,
  260. # invoke say, otherwise invoke say_status. Differently from say and
  261. # similarly to say_status, this method respects the quiet? option given.
  262. 16 def log(*args) # :doc:
  263. if args.size == 1
  264. say args.first.to_s unless options.quiet?
  265. else
  266. args << (behavior == :invoke ? :green : :red)
  267. say_status(*args)
  268. end
  269. end
  270. # Runs the supplied command using either "rake ..." or "rails ..."
  271. # based on the executor parameter provided.
  272. 16 def execute_command(executor, command, options = {}) # :doc:
  273. log executor, command
  274. sudo = options[:sudo] && !Gem.win_platform? ? "sudo " : ""
  275. config = {
  276. env: { "RAILS_ENV" => (options[:env] || ENV["RAILS_ENV"] || "development") },
  277. verbose: false,
  278. capture: options[:capture],
  279. abort_on_failure: options[:abort_on_failure],
  280. }
  281. in_root { run("#{sudo}#{extify(executor)} #{command}", config) }
  282. end
  283. # Add an extension to the given name based on the platform.
  284. 16 def extify(name) # :doc:
  285. if Gem.win_platform?
  286. "#{name}.bat"
  287. else
  288. name
  289. end
  290. end
  291. # Surround string with single quotes if there is no quotes.
  292. # Otherwise fall back to double quotes
  293. 16 def quote(value) # :doc:
  294. if value.respond_to? :each_pair
  295. return value.map do |k, v|
  296. "#{k}: #{quote(v)}"
  297. end.join(", ")
  298. end
  299. return value.inspect unless value.is_a? String
  300. if value.include?("'")
  301. value.inspect
  302. else
  303. "'#{value}'"
  304. end
  305. end
  306. # Returns optimized string with indentation
  307. 16 def optimize_indentation(value, amount = 0) # :doc:
  308. return "#{value}\n" unless value.is_a?(String)
  309. "#{value.strip_heredoc.indent(amount).chomp}\n"
  310. end
  311. # Indent the +Gemfile+ to the depth of @indentation
  312. 16 def indentation # :doc:
  313. " " * @indentation
  314. end
  315. # Manage +Gemfile+ indentation for a DSL action block
  316. 16 def with_indentation(&block) # :doc:
  317. @indentation += 1
  318. instance_eval(&block)
  319. ensure
  320. @indentation -= 1
  321. end
  322. # Append string to a file with a newline if necessary
  323. 16 def append_file_with_newline(path, str, options = {})
  324. gsub_file path, /\n?\z/, options do |match|
  325. match.end_with?("\n") ? "" : "\n#{str}\n"
  326. end
  327. end
  328. end
  329. end
  330. end

lib/rails/generators/actions/create_migration.rb

38.64% lines covered

44 relevant lines. 17 lines covered and 27 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "fileutils"
  3. 1 require "thor/actions"
  4. 1 module Rails
  5. 1 module Generators
  6. 1 module Actions
  7. 1 class CreateMigration < Thor::Actions::CreateFile #:nodoc:
  8. 1 def migration_dir
  9. File.dirname(@destination)
  10. end
  11. 1 def migration_file_name
  12. @base.migration_file_name
  13. end
  14. 1 def identical?
  15. exists? && File.binread(existing_migration) == render
  16. end
  17. 1 def invoke!
  18. invoked_file = super
  19. File.exist?(@destination) ? invoked_file : relative_existing_migration
  20. end
  21. 1 def revoke!
  22. say_destination = exists? ? relative_existing_migration : relative_destination
  23. say_status :remove, :red, say_destination
  24. return unless exists?
  25. ::FileUtils.rm_rf(existing_migration) unless pretend?
  26. existing_migration
  27. end
  28. 1 def relative_existing_migration
  29. base.relative_to_original_destination_root(existing_migration)
  30. end
  31. 1 def existing_migration
  32. @existing_migration ||= begin
  33. @base.class.migration_exists?(migration_dir, migration_file_name) ||
  34. File.exist?(@destination) && @destination
  35. end
  36. end
  37. 1 alias :exists? :existing_migration
  38. 1 private
  39. 1 def on_conflict_behavior # :doc:
  40. options = base.options.merge(config)
  41. if identical?
  42. say_status :identical, :blue, relative_existing_migration
  43. elsif options[:force]
  44. say_status :remove, :green, relative_existing_migration
  45. say_status :create, :green
  46. unless pretend?
  47. ::FileUtils.rm_rf(existing_migration)
  48. yield
  49. end
  50. elsif options[:skip]
  51. say_status :skip, :yellow
  52. else
  53. say_status :conflict, :red
  54. raise Error, "Another migration is already named #{migration_file_name}: " \
  55. "#{existing_migration}. Use --force to replace this migration " \
  56. "or --skip to ignore conflicted file."
  57. end
  58. end
  59. 1 def say_status(status, color, message = relative_destination) # :doc:
  60. base.shell.say_status(status, message, color) if config[:verbose]
  61. end
  62. end
  63. end
  64. end
  65. end

lib/rails/generators/active_model.rb

54.55% lines covered

22 relevant lines. 12 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 3 module Rails
  3. 3 module Generators
  4. # ActiveModel is a class to be implemented by each ORM to allow Rails to
  5. # generate customized controller code.
  6. #
  7. # The API has the same methods as ActiveRecord, but each method returns a
  8. # string that matches the ORM API.
  9. #
  10. # For example:
  11. #
  12. # ActiveRecord::Generators::ActiveModel.find(Foo, "params[:id]")
  13. # # => "Foo.find(params[:id])"
  14. #
  15. # DataMapper::Generators::ActiveModel.find(Foo, "params[:id]")
  16. # # => "Foo.get(params[:id])"
  17. #
  18. # On initialization, the ActiveModel accepts the instance name that will
  19. # receive the calls:
  20. #
  21. # builder = ActiveRecord::Generators::ActiveModel.new "@foo"
  22. # builder.save # => "@foo.save"
  23. #
  24. # The only exception in ActiveModel for ActiveRecord is the use of self.build
  25. # instead of self.new.
  26. #
  27. 3 class ActiveModel
  28. 3 attr_reader :name
  29. 3 def initialize(name)
  30. @name = name
  31. end
  32. # GET index
  33. 3 def self.all(klass)
  34. "#{klass}.all"
  35. end
  36. # GET show
  37. # GET edit
  38. # PATCH/PUT update
  39. # DELETE destroy
  40. 3 def self.find(klass, params = nil)
  41. "#{klass}.find(#{params})"
  42. end
  43. # GET new
  44. # POST create
  45. 3 def self.build(klass, params = nil)
  46. if params
  47. "#{klass}.new(#{params})"
  48. else
  49. "#{klass}.new"
  50. end
  51. end
  52. # POST create
  53. 3 def save
  54. "#{name}.save"
  55. end
  56. # PATCH/PUT update
  57. 3 def update(params = nil)
  58. "#{name}.update(#{params})"
  59. end
  60. # POST create
  61. # PATCH/PUT update
  62. 3 def errors
  63. "#{name}.errors"
  64. end
  65. # DELETE destroy
  66. 3 def destroy
  67. "#{name}.destroy"
  68. end
  69. end
  70. end
  71. end

lib/rails/generators/app_base.rb

43.93% lines covered

214 relevant lines. 94 lines covered and 120 lines missed.
    
  1. # frozen_string_literal: true
  2. 3 require "fileutils"
  3. 3 require "digest/md5"
  4. 3 require "rails/version" unless defined?(Rails::VERSION)
  5. 3 require "open-uri"
  6. 3 require "uri"
  7. 3 require "rails/generators"
  8. 3 require "active_support/core_ext/array/extract_options"
  9. 3 module Rails
  10. 3 module Generators
  11. 3 class AppBase < Base # :nodoc:
  12. 3 include Database
  13. 3 include AppName
  14. 3 attr_accessor :rails_template
  15. 3 add_shebang_option!
  16. 3 argument :app_path, type: :string
  17. 3 def self.strict_args_position
  18. false
  19. end
  20. 3 def self.add_shared_options_for(name)
  21. 4 class_option :template, type: :string, aliases: "-m",
  22. desc: "Path to some #{name} template (can be a filesystem path or URL)"
  23. 4 class_option :database, type: :string, aliases: "-d", default: "sqlite3",
  24. desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
  25. 4 class_option :skip_gemfile, type: :boolean, default: false,
  26. desc: "Don't create a Gemfile"
  27. 4 class_option :skip_git, type: :boolean, aliases: "-G", default: false,
  28. desc: "Skip .gitignore file"
  29. 4 class_option :skip_keeps, type: :boolean, default: false,
  30. desc: "Skip source control .keep files"
  31. 4 class_option :skip_action_mailer, type: :boolean, aliases: "-M",
  32. default: false,
  33. desc: "Skip Action Mailer files"
  34. 4 class_option :skip_action_mailbox, type: :boolean, default: false,
  35. desc: "Skip Action Mailbox gem"
  36. 4 class_option :skip_action_text, type: :boolean, default: false,
  37. desc: "Skip Action Text gem"
  38. 4 class_option :skip_active_record, type: :boolean, aliases: "-O", default: false,
  39. desc: "Skip Active Record files"
  40. 4 class_option :skip_active_job, type: :boolean, default: false,
  41. desc: "Skip Active Job"
  42. 4 class_option :skip_active_storage, type: :boolean, default: false,
  43. desc: "Skip Active Storage files"
  44. 4 class_option :skip_puma, type: :boolean, aliases: "-P", default: false,
  45. desc: "Skip Puma related files"
  46. 4 class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false,
  47. desc: "Skip Action Cable files"
  48. 4 class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false,
  49. desc: "Skip Sprockets files"
  50. 4 class_option :skip_spring, type: :boolean, default: false,
  51. desc: "Don't install Spring application preloader"
  52. 4 class_option :skip_listen, type: :boolean, default: false,
  53. desc: "Don't generate configuration that depends on the listen gem"
  54. 4 class_option :skip_javascript, type: :boolean, aliases: "-J", default: name == "plugin",
  55. desc: "Skip JavaScript files"
  56. 4 class_option :skip_turbolinks, type: :boolean, default: false,
  57. desc: "Skip turbolinks gem"
  58. 4 class_option :skip_jbuilder, type: :boolean, default: false,
  59. desc: "Skip jbuilder gem"
  60. 4 class_option :skip_test, type: :boolean, aliases: "-T", default: false,
  61. desc: "Skip test files"
  62. 4 class_option :skip_system_test, type: :boolean, default: false,
  63. desc: "Skip system test files"
  64. 4 class_option :skip_bootsnap, type: :boolean, default: false,
  65. desc: "Skip bootsnap gem"
  66. 4 class_option :dev, type: :boolean, default: false,
  67. desc: "Set up the #{name} with Gemfile pointing to your Rails checkout"
  68. 4 class_option :edge, type: :boolean, default: false,
  69. desc: "Set up the #{name} with Gemfile pointing to Rails repository"
  70. 4 class_option :master, type: :boolean, default: false,
  71. desc: "Set up the #{name} with Gemfile pointing to Rails repository master branch"
  72. 4 class_option :rc, type: :string, default: nil,
  73. desc: "Path to file containing extra configuration options for rails command"
  74. 4 class_option :no_rc, type: :boolean, default: false,
  75. desc: "Skip loading of extra configuration options from .railsrc file"
  76. 4 class_option :help, type: :boolean, aliases: "-h", group: :rails,
  77. desc: "Show this help message and quit"
  78. end
  79. 3 def initialize(*args)
  80. @gem_filter = lambda { |gem| true }
  81. @extra_entries = []
  82. super
  83. end
  84. 3 private
  85. 3 def gemfile_entry(name, *args) # :doc:
  86. options = args.extract_options!
  87. version = args.first
  88. github = options[:github]
  89. path = options[:path]
  90. if github
  91. @extra_entries << GemfileEntry.github(name, github)
  92. elsif path
  93. @extra_entries << GemfileEntry.path(name, path)
  94. else
  95. @extra_entries << GemfileEntry.version(name, version)
  96. end
  97. self
  98. end
  99. 3 def gemfile_entries # :doc:
  100. [rails_gemfile_entry,
  101. database_gemfile_entry,
  102. web_server_gemfile_entry,
  103. assets_gemfile_entry,
  104. webpacker_gemfile_entry,
  105. javascript_gemfile_entry,
  106. jbuilder_gemfile_entry,
  107. psych_gemfile_entry,
  108. cable_gemfile_entry,
  109. @extra_entries].flatten.find_all(&@gem_filter)
  110. end
  111. 3 def add_gem_entry_filter # :doc:
  112. @gem_filter = lambda { |next_filter, entry|
  113. yield(entry) && next_filter.call(entry)
  114. }.curry[@gem_filter]
  115. end
  116. 3 def builder # :doc:
  117. @builder ||= begin
  118. builder_class = get_builder_class
  119. builder_class.include(ActionMethods)
  120. builder_class.new(self)
  121. end
  122. end
  123. 3 def build(meth, *args) # :doc:
  124. builder.send(meth, *args) if builder.respond_to?(meth)
  125. end
  126. 3 def create_root # :doc:
  127. valid_const?
  128. empty_directory "."
  129. FileUtils.cd(destination_root) unless options[:pretend]
  130. end
  131. 3 def apply_rails_template # :doc:
  132. apply rails_template if rails_template
  133. rescue Thor::Error, LoadError, Errno::ENOENT => e
  134. raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}"
  135. end
  136. 3 def set_default_accessors! # :doc:
  137. self.destination_root = File.expand_path(app_path, destination_root)
  138. self.rails_template = \
  139. case options[:template]
  140. when /^https?:\/\//
  141. options[:template]
  142. when String
  143. File.expand_path(options[:template], Dir.pwd)
  144. else
  145. options[:template]
  146. end
  147. end
  148. 3 def database_gemfile_entry # :doc:
  149. return [] if options[:skip_active_record]
  150. gem_name, gem_version = gem_for_database
  151. GemfileEntry.version gem_name, gem_version,
  152. "Use #{options[:database]} as the database for Active Record"
  153. end
  154. 3 def web_server_gemfile_entry # :doc:
  155. return [] if options[:skip_puma]
  156. comment = "Use Puma as the app server"
  157. GemfileEntry.new("puma", "~> 4.1", comment)
  158. end
  159. 3 def include_all_railties? # :doc:
  160. [
  161. options.values_at(
  162. :skip_active_record,
  163. :skip_action_mailer,
  164. :skip_test,
  165. :skip_sprockets,
  166. :skip_action_cable,
  167. :skip_active_job
  168. ),
  169. skip_active_storage?,
  170. skip_action_mailbox?,
  171. skip_action_text?
  172. ].flatten.none?
  173. end
  174. 3 def comment_if(value) # :doc:
  175. question = "#{value}?"
  176. comment =
  177. if respond_to?(question, true)
  178. send(question)
  179. else
  180. options[value]
  181. end
  182. comment ? "# " : ""
  183. end
  184. 3 def keeps? # :doc:
  185. !options[:skip_keeps]
  186. end
  187. 3 def sqlite3? # :doc:
  188. !options[:skip_active_record] && options[:database] == "sqlite3"
  189. end
  190. 3 def skip_active_storage? # :doc:
  191. options[:skip_active_storage] || options[:skip_active_record]
  192. end
  193. 3 def skip_action_mailbox? # :doc:
  194. options[:skip_action_mailbox] || skip_active_storage?
  195. end
  196. 3 def skip_action_text? # :doc:
  197. options[:skip_action_text] || skip_active_storage?
  198. end
  199. 3 def skip_dev_gems? # :doc:
  200. options[:skip_dev_gems]
  201. end
  202. 3 class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
  203. 3 def initialize(name, version, comment, options = {}, commented_out = false)
  204. super
  205. end
  206. 3 def self.github(name, github, branch = nil, comment = nil)
  207. if branch
  208. new(name, nil, comment, github: github, branch: branch)
  209. else
  210. new(name, nil, comment, github: github)
  211. end
  212. end
  213. 3 def self.version(name, version, comment = nil)
  214. new(name, version, comment)
  215. end
  216. 3 def self.path(name, path, comment = nil)
  217. new(name, nil, comment, path: path)
  218. end
  219. 3 def version
  220. version = super
  221. if version.is_a?(Array)
  222. version.join("', '")
  223. else
  224. version
  225. end
  226. end
  227. end
  228. 3 def rails_gemfile_entry
  229. if options.dev?
  230. [
  231. GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH)
  232. ]
  233. elsif options.edge?
  234. [
  235. GemfileEntry.github("rails", "rails/rails")
  236. ]
  237. elsif options.master?
  238. [
  239. GemfileEntry.github("rails", "rails/rails", "master")
  240. ]
  241. else
  242. [GemfileEntry.version("rails",
  243. rails_version_specifier,
  244. "Bundle edge Rails instead: gem 'rails', github: 'rails/rails'")]
  245. end
  246. end
  247. 3 def rails_version_specifier(gem_version = Rails.gem_version)
  248. if gem_version.segments.size == 3 || gem_version.release.segments.size == 3
  249. # ~> 1.2.3
  250. # ~> 1.2.3.pre4
  251. "~> #{gem_version}"
  252. else
  253. # ~> 1.2.3, >= 1.2.3.4
  254. # ~> 1.2.3, >= 1.2.3.4.pre5
  255. patch = gem_version.segments[0, 3].join(".")
  256. ["~> #{patch}", ">= #{gem_version}"]
  257. end
  258. end
  259. 3 def assets_gemfile_entry
  260. return [] if options[:skip_sprockets]
  261. GemfileEntry.version("sass-rails", ">= 6", "Use SCSS for stylesheets")
  262. end
  263. 3 def webpacker_gemfile_entry
  264. return [] if options[:skip_javascript]
  265. if options.dev? || options.edge? || options.master?
  266. GemfileEntry.github "webpacker", "rails/webpacker", nil, "Use development version of Webpacker"
  267. else
  268. GemfileEntry.version "webpacker", "~> 5.0", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
  269. end
  270. end
  271. 3 def jbuilder_gemfile_entry
  272. return [] if options[:skip_jbuilder]
  273. comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
  274. GemfileEntry.new "jbuilder", "~> 2.7", comment, {}, options[:api]
  275. end
  276. 3 def javascript_gemfile_entry
  277. if options[:skip_javascript] || options[:skip_turbolinks]
  278. []
  279. else
  280. [ GemfileEntry.version("turbolinks", "~> 5",
  281. "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") ]
  282. end
  283. end
  284. 3 def psych_gemfile_entry
  285. return [] unless defined?(Rubinius)
  286. comment = "Use Psych as the YAML engine, instead of Syck, so serialized " \
  287. "data can be read safely from different rubies (see http://git.io/uuLVag)"
  288. GemfileEntry.new("psych", "~> 2.0", comment, platforms: :rbx)
  289. end
  290. 3 def cable_gemfile_entry
  291. return [] if options[:skip_action_cable]
  292. comment = "Use Redis adapter to run Action Cable in production"
  293. gems = []
  294. gems << GemfileEntry.new("redis", "~> 4.0", comment, {}, true)
  295. gems
  296. end
  297. 3 def bundle_command(command, env = {})
  298. say_status :run, "bundle #{command}"
  299. # We are going to shell out rather than invoking Bundler::CLI.new(command)
  300. # because `rails new` loads the Thor gem and on the other hand bundler uses
  301. # its own vendored Thor, which could be a different version. Running both
  302. # things in the same process is a recipe for a night with paracetamol.
  303. #
  304. # Thanks to James Tucker for the Gem tricks involved in this call.
  305. _bundle_command = Gem.bin_path("bundler", "bundle")
  306. require "bundler"
  307. Bundler.with_original_env do
  308. exec_bundle_command(_bundle_command, command, env)
  309. end
  310. end
  311. 3 def exec_bundle_command(bundle_command, command, env)
  312. full_command = %Q["#{Gem.ruby}" "#{bundle_command}" #{command}]
  313. if options[:quiet]
  314. system(env, full_command, out: File::NULL)
  315. else
  316. system(env, full_command)
  317. end
  318. end
  319. 3 def bundle_install?
  320. !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
  321. end
  322. 3 def spring_install?
  323. !options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
  324. end
  325. 3 def webpack_install?
  326. !(options[:skip_javascript] || options[:skip_webpack_install])
  327. end
  328. 3 def depends_on_system_test?
  329. !(options[:skip_system_test] || options[:skip_test] || options[:api])
  330. end
  331. 3 def depend_on_listen?
  332. !options[:skip_listen] && os_supports_listen_out_of_the_box?
  333. end
  334. 3 def depend_on_bootsnap?
  335. !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION)
  336. end
  337. 3 def os_supports_listen_out_of_the_box?
  338. /darwin|linux/.match?(RbConfig::CONFIG["host_os"])
  339. end
  340. 3 def run_bundle
  341. bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1") if bundle_install?
  342. end
  343. 3 def run_webpack
  344. if webpack_install?
  345. rails_command "webpacker:install"
  346. if options[:webpack] && options[:webpack] != "webpack"
  347. rails_command "webpacker:install:#{options[:webpack]}"
  348. end
  349. end
  350. end
  351. 3 def generate_bundler_binstub
  352. if bundle_install?
  353. bundle_command("binstubs bundler")
  354. end
  355. end
  356. 3 def generate_spring_binstub
  357. if bundle_install? && spring_install?
  358. bundle_command("exec spring binstub")
  359. end
  360. end
  361. 3 def empty_directory_with_keep_file(destination, config = {})
  362. empty_directory(destination, config)
  363. keep_file(destination)
  364. end
  365. 3 def keep_file(destination)
  366. create_file("#{destination}/.keep") if keeps?
  367. end
  368. end
  369. end
  370. end

lib/rails/generators/app_name.rb

53.85% lines covered

26 relevant lines. 14 lines covered and 12 lines missed.
    
  1. # frozen_string_literal: true
  2. 3 module Rails
  3. 3 module Generators
  4. 3 module AppName # :nodoc:
  5. 3 RESERVED_NAMES = %w(application destroy plugin runner test)
  6. 3 private
  7. 3 def app_name
  8. @app_name ||= original_app_name.tr('\\', "").tr("-. ", "_")
  9. end
  10. 3 def original_app_name
  11. @original_app_name ||= defined_app_const_base? ? defined_app_name : File.basename(destination_root)
  12. end
  13. 3 def defined_app_name
  14. defined_app_const_base.underscore
  15. end
  16. 3 def defined_app_const_base
  17. Rails.respond_to?(:application) && defined?(Rails::Application) &&
  18. Rails.application.is_a?(Rails::Application) && Rails.application.class.name.chomp("::Application")
  19. end
  20. 3 alias :defined_app_const_base? :defined_app_const_base
  21. 3 def app_const_base
  22. @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, "_").squeeze("_").camelize
  23. end
  24. 3 alias :camelized :app_const_base
  25. 3 def app_const
  26. @app_const ||= "#{app_const_base}::Application"
  27. end
  28. 3 def valid_const?
  29. if /^\d/.match?(app_const)
  30. raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers."
  31. elsif RESERVED_NAMES.include?(original_app_name)
  32. raise Error, "Invalid application name #{original_app_name}. Please give a " \
  33. "name which does not match one of the reserved rails " \
  34. "words: #{RESERVED_NAMES.join(", ")}"
  35. elsif Object.const_defined?(app_const_base)
  36. raise Error, "Invalid application name #{original_app_name}, constant #{app_const_base} is already in use. Please choose another application name."
  37. end
  38. end
  39. end
  40. end
  41. end

lib/rails/generators/base.rb

63.82% lines covered

152 relevant lines. 97 lines covered and 55 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 begin
  3. 16 require "thor/group"
  4. rescue LoadError
  5. puts "Thor is not available.\nIf you ran this command from a git checkout " \
  6. "of Rails, please make sure thor is installed,\nand run this command " \
  7. "as `ruby #{$0} #{(ARGV | ['--dev']).join(" ")}`"
  8. exit
  9. end
  10. 16 module Rails
  11. 16 module Generators
  12. 16 class Error < Thor::Error # :nodoc:
  13. end
  14. 16 class Base < Thor::Group
  15. 16 include Thor::Actions
  16. 16 include Rails::Generators::Actions
  17. 16 class_option :skip_namespace, type: :boolean, default: false,
  18. desc: "Skip namespace (affects only isolated engines)"
  19. 16 class_option :skip_collision_check, type: :boolean, default: false,
  20. desc: "Skip collision check"
  21. 16 add_runtime_options!
  22. 16 strict_args_position!
  23. 16 def self.exit_on_failure? # :nodoc:
  24. false
  25. end
  26. # Returns the source root for this generator using default_source_root as default.
  27. 16 def self.source_root(path = nil)
  28. 42 @_source_root = path if path
  29. 42 @_source_root ||= default_source_root
  30. end
  31. # Tries to get the description from a USAGE file one folder above the source
  32. # root otherwise uses a default description.
  33. 16 def self.desc(description = nil)
  34. return super if description
  35. @desc ||= if usage_path
  36. ERB.new(File.read(usage_path)).result(binding)
  37. else
  38. "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
  39. end
  40. end
  41. # Convenience method to get the namespace from the class name. It's the
  42. # same as Thor default except that the Generator at the end of the class
  43. # is removed.
  44. 16 def self.namespace(name = nil)
  45. return super if name
  46. @namespace ||= super.delete_suffix("_generator").sub(/:generators:/, ":")
  47. end
  48. # Convenience method to hide this generator from the available ones when
  49. # running rails generator command.
  50. 16 def self.hide!
  51. Rails::Generators.hide_namespace(namespace)
  52. end
  53. # Invoke a generator based on the value supplied by the user to the
  54. # given option named "name". A class option is created when this method
  55. # is invoked and you can set a hash to customize it.
  56. #
  57. # ==== Examples
  58. #
  59. # module Rails::Generators
  60. # class ControllerGenerator < Base
  61. # hook_for :test_framework, aliases: "-t"
  62. # end
  63. # end
  64. #
  65. # The example above will create a test framework option and will invoke
  66. # a generator based on the user supplied value.
  67. #
  68. # For example, if the user invoke the controller generator as:
  69. #
  70. # bin/rails generate controller Account --test-framework=test_unit
  71. #
  72. # The controller generator will then try to invoke the following generators:
  73. #
  74. # "rails:test_unit", "test_unit:controller", "test_unit"
  75. #
  76. # Notice that "rails:generators:test_unit" could be loaded as well, what
  77. # Rails looks for is the first and last parts of the namespace. This is what
  78. # allows any test framework to hook into Rails as long as it provides any
  79. # of the hooks above.
  80. #
  81. # ==== Options
  82. #
  83. # The first and last part used to find the generator to be invoked are
  84. # guessed based on class invokes hook_for, as noticed in the example above.
  85. # This can be customized with two options: :in and :as.
  86. #
  87. # Let's suppose you are creating a generator that needs to invoke the
  88. # controller generator from test unit. Your first attempt is:
  89. #
  90. # class AwesomeGenerator < Rails::Generators::Base
  91. # hook_for :test_framework
  92. # end
  93. #
  94. # The lookup in this case for test_unit as input is:
  95. #
  96. # "test_unit:awesome", "test_unit"
  97. #
  98. # Which is not the desired lookup. You can change it by providing the
  99. # :as option:
  100. #
  101. # class AwesomeGenerator < Rails::Generators::Base
  102. # hook_for :test_framework, as: :controller
  103. # end
  104. #
  105. # And now it will look up at:
  106. #
  107. # "test_unit:controller", "test_unit"
  108. #
  109. # Similarly, if you want it to also look up in the rails namespace, you
  110. # just need to provide the :in value:
  111. #
  112. # class AwesomeGenerator < Rails::Generators::Base
  113. # hook_for :test_framework, in: :rails, as: :controller
  114. # end
  115. #
  116. # And the lookup is exactly the same as previously:
  117. #
  118. # "rails:test_unit", "test_unit:controller", "test_unit"
  119. #
  120. # ==== Switches
  121. #
  122. # All hooks come with switches for user interface. If you do not want
  123. # to use any test framework, you can do:
  124. #
  125. # bin/rails generate controller Account --skip-test-framework
  126. #
  127. # Or similarly:
  128. #
  129. # bin/rails generate controller Account --no-test-framework
  130. #
  131. # ==== Boolean hooks
  132. #
  133. # In some cases, you may want to provide a boolean hook. For example, webrat
  134. # developers might want to have webrat available on controller generator.
  135. # This can be achieved as:
  136. #
  137. # Rails::Generators::ControllerGenerator.hook_for :webrat, type: :boolean
  138. #
  139. # Then, if you want webrat to be invoked, just supply:
  140. #
  141. # bin/rails generate controller Account --webrat
  142. #
  143. # The hooks lookup is similar as above:
  144. #
  145. # "rails:generators:webrat", "webrat:generators:controller", "webrat"
  146. #
  147. # ==== Custom invocations
  148. #
  149. # You can also supply a block to hook_for to customize how the hook is
  150. # going to be invoked. The block receives two arguments, an instance
  151. # of the current class and the class to be invoked.
  152. #
  153. # For example, in the resource generator, the controller should be invoked
  154. # with a pluralized class name. But by default it is invoked with the same
  155. # name as the resource generator, which is singular. To change this, we
  156. # can give a block to customize how the controller can be invoked.
  157. #
  158. # hook_for :resource_controller do |instance, controller|
  159. # instance.invoke controller, [ instance.name.pluralize ]
  160. # end
  161. #
  162. 16 def self.hook_for(*names, &block)
  163. 23 options = names.extract_options!
  164. 23 in_base = options.delete(:in) || base_name
  165. 23 as_hook = options.delete(:as) || generator_name
  166. 23 names.each do |name|
  167. 31 unless class_options.key?(name)
  168. 24 defaults = if options[:type] == :boolean
  169. {}
  170. 24 elsif [true, false].include?(default_value_for_option(name, options))
  171. 2 { banner: "" }
  172. else
  173. 22 { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
  174. end
  175. 24 class_option(name, defaults.merge!(options))
  176. end
  177. 31 hooks[name] = [ in_base, as_hook ]
  178. 31 invoke_from_option(name, options, &block)
  179. end
  180. end
  181. # Remove a previously added hook.
  182. #
  183. # remove_hook_for :orm
  184. 16 def self.remove_hook_for(*names)
  185. 1 remove_invocation(*names)
  186. 1 names.each do |name|
  187. 1 hooks.delete(name)
  188. end
  189. end
  190. # Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
  191. 16 def self.class_option(name, options = {}) #:nodoc:
  192. 188 options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
  193. 188 options[:aliases] = default_aliases_for_option(name, options)
  194. 188 options[:default] = default_value_for_option(name, options)
  195. 188 super(name, options)
  196. end
  197. # Returns the default source root for a given generator. This is used internally
  198. # by rails to set its generators source root. If you want to customize your source
  199. # root, you should use source_root.
  200. 16 def self.default_source_root
  201. 39 return unless base_name && generator_name
  202. 39 return unless default_generator_root
  203. 19 path = File.join(default_generator_root, "templates")
  204. 19 path if File.exist?(path)
  205. end
  206. # Returns the base root for a common set of generators. This is used to dynamically
  207. # guess the default source root.
  208. 16 def self.base_root
  209. 58 __dir__
  210. end
  211. # Cache source root and add lib/generators/base/generator/templates to
  212. # source paths.
  213. 16 def self.inherited(base) #:nodoc:
  214. 39 super
  215. # Invoke source_root so the default_source_root is set.
  216. 39 base.source_root
  217. 39 if base.name && !base.name.end_with?("Base")
  218. 23 Rails::Generators.subclasses << base
  219. 23 Rails::Generators.templates_path.each do |path|
  220. 23 if base.name.include?("::")
  221. 23 base.source_paths << File.join(path, base.base_name, base.generator_name)
  222. else
  223. base.source_paths << File.join(path, base.generator_name)
  224. end
  225. end
  226. end
  227. end
  228. 16 private
  229. # Check whether the given class names are already taken by user
  230. # application or Ruby on Rails.
  231. 16 def class_collisions(*class_names)
  232. return unless behavior == :invoke
  233. return if options.skip_collision_check?
  234. return if options.force?
  235. class_names.flatten.each do |class_name|
  236. class_name = class_name.to_s
  237. next if class_name.strip.empty?
  238. # Split the class from its module nesting
  239. nesting = class_name.split("::")
  240. last_name = nesting.pop
  241. last = extract_last_module(nesting)
  242. if last && last.const_defined?(last_name.camelize, false)
  243. raise Error, "The name '#{class_name}' is either already used in your application " \
  244. "or reserved by Ruby on Rails. Please choose an alternative or use --skip-collision-check " \
  245. "or --force to skip this check and run this generator again."
  246. end
  247. end
  248. end
  249. # Takes in an array of nested modules and extracts the last module
  250. 16 def extract_last_module(nesting) # :doc:
  251. nesting.inject(Object) do |last_module, nest|
  252. break unless last_module.const_defined?(nest, false)
  253. last_module.const_get(nest)
  254. end
  255. end
  256. # Wrap block with namespace of current application
  257. # if namespace exists and is not skipped
  258. 16 def module_namespacing(&block) # :doc:
  259. content = capture(&block)
  260. content = wrap_with_namespace(content) if namespaced?
  261. concat(content)
  262. end
  263. 16 def indent(content, multiplier = 2) # :doc:
  264. spaces = " " * multiplier
  265. content.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join
  266. end
  267. 16 def wrap_with_namespace(content) # :doc:
  268. content = indent(content).chomp
  269. "module #{namespace.name}\n#{content}\nend\n"
  270. end
  271. 16 def namespace # :doc:
  272. Rails::Generators.namespace
  273. end
  274. 16 def namespaced? # :doc:
  275. !options[:skip_namespace] && namespace
  276. end
  277. 16 def namespace_dirs
  278. @namespace_dirs ||= namespace.name.split("::").map(&:underscore)
  279. end
  280. 16 def namespaced_path # :doc:
  281. @namespaced_path ||= namespace_dirs.join("/")
  282. end
  283. # Use Rails default banner.
  284. 16 def self.banner # :doc:
  285. "rails generate #{namespace.delete_prefix("rails:")} #{arguments.map(&:usage).join(' ')} [options]".gsub(/\s+/, " ")
  286. end
  287. # Sets the base_name taking into account the current class namespace.
  288. 16 def self.base_name # :doc:
  289. 943 @base_name ||= begin
  290. 39 if base = name.to_s.split("::").first
  291. 39 base.underscore
  292. end
  293. end
  294. end
  295. # Removes the namespaces and get the generator name. For example,
  296. # Rails::Generators::ModelGenerator will return "model" as generator name.
  297. 16 def self.generator_name # :doc:
  298. 938 @generator_name ||= begin
  299. 39 if generator = name.to_s.split("::").last
  300. 39 generator.delete_suffix!("Generator")
  301. 39 generator.underscore
  302. end
  303. end
  304. end
  305. # Returns the default value for the option name given doing a lookup in
  306. # Rails::Generators.options.
  307. 16 def self.default_value_for_option(name, options) # :doc:
  308. 212 default_for_option(Rails::Generators.options, name, options, options[:default])
  309. end
  310. # Returns default aliases for the option name given doing a lookup in
  311. # Rails::Generators.aliases.
  312. 16 def self.default_aliases_for_option(name, options) # :doc:
  313. 188 default_for_option(Rails::Generators.aliases, name, options, options[:aliases])
  314. end
  315. # Returns default for the option name given doing a lookup in config.
  316. 16 def self.default_for_option(config, name, options, default) # :doc:
  317. 400 if generator_name && (c = config[generator_name.to_sym]) && c.key?(name)
  318. c[name]
  319. 400 elsif base_name && (c = config[base_name.to_sym]) && c.key?(name)
  320. 95 c[name]
  321. 305 elsif config[:rails].key?(name)
  322. config[:rails][name]
  323. else
  324. 305 default
  325. end
  326. end
  327. # Keep hooks configuration that are used on prepare_for_invocation.
  328. 16 def self.hooks #:nodoc:
  329. 61 @hooks ||= from_superclass(:hooks, {})
  330. end
  331. # Prepare class invocation to search on Rails namespace if a previous
  332. # added hook is being used.
  333. 16 def self.prepare_for_invocation(name, value) #:nodoc:
  334. return super unless value.is_a?(String) || value.is_a?(Symbol)
  335. if value && constants = hooks[name]
  336. value = name if TrueClass === value
  337. Rails::Generators.find_by_namespace(value, *constants)
  338. elsif klass = Rails::Generators.find_by_namespace(value)
  339. klass
  340. else
  341. super
  342. end
  343. end
  344. # Small macro to add ruby as an option to the generator with proper
  345. # default value plus an instance helper method called shebang.
  346. 16 def self.add_shebang_option! # :doc:
  347. 3 class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command,
  348. desc: "Path to the Ruby binary of your choice", banner: "PATH"
  349. 3 no_tasks {
  350. 3 define_method :shebang do
  351. @shebang ||= begin
  352. command = if options[:ruby] == Thor::Util.ruby_command
  353. "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}"
  354. else
  355. options[:ruby]
  356. end
  357. "#!#{command}"
  358. end
  359. end
  360. }
  361. end
  362. 16 def self.usage_path # :doc:
  363. paths = [
  364. source_root && File.expand_path("../USAGE", source_root),
  365. default_generator_root && File.join(default_generator_root, "USAGE")
  366. ]
  367. paths.compact.detect { |path| File.exist? path }
  368. end
  369. 16 def self.default_generator_root # :doc:
  370. 58 path = File.expand_path(File.join(base_name, generator_name), base_root)
  371. 58 path if File.exist?(path)
  372. end
  373. end
  374. end
  375. end

lib/rails/generators/css/assets/assets_generator.rb

0.0% lines covered

11 relevant lines. 0 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/named_base"
  3. module Css # :nodoc:
  4. module Generators # :nodoc:
  5. class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
  6. source_root File.expand_path("templates", __dir__)
  7. def copy_stylesheet
  8. copy_file "stylesheet.css", File.join("app/assets/stylesheets", class_path, "#{file_name}.css")
  9. end
  10. end
  11. end
  12. end

lib/rails/generators/css/scaffold/scaffold_generator.rb

0.0% lines covered

11 relevant lines. 0 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/named_base"
  3. module Css # :nodoc:
  4. module Generators # :nodoc:
  5. class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc:
  6. source_root Rails::Generators::ScaffoldGenerator.source_root
  7. # In order to allow the Sass generators to pick up the default Rails CSS and
  8. # transform it, we leave it in a standard location for the CSS stylesheet
  9. # generators to handle. For the simple, default case, just copy it over.
  10. def copy_stylesheet
  11. copy_file "scaffold.css", "app/assets/stylesheets/scaffold.css"
  12. end
  13. end
  14. end
  15. end

lib/rails/generators/database.rb

31.25% lines covered

32 relevant lines. 10 lines covered and 22 lines missed.
    
  1. # frozen_string_literal: true
  2. 3 module Rails
  3. 3 module Generators
  4. 3 module Database # :nodoc:
  5. 3 JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
  6. 3 DATABASES = %w( mysql postgresql sqlite3 oracle sqlserver ) + JDBC_DATABASES
  7. 3 def initialize(*)
  8. super
  9. convert_database_option_for_jruby
  10. end
  11. 3 def gem_for_database(database = options[:database])
  12. case database
  13. when "mysql" then ["mysql2", ["~> 0.5"]]
  14. when "postgresql" then ["pg", ["~> 1.1"]]
  15. when "sqlite3" then ["sqlite3", ["~> 1.4"]]
  16. when "oracle" then ["activerecord-oracle_enhanced-adapter", nil]
  17. when "sqlserver" then ["activerecord-sqlserver-adapter", nil]
  18. when "jdbcmysql" then ["activerecord-jdbcmysql-adapter", nil]
  19. when "jdbcsqlite3" then ["activerecord-jdbcsqlite3-adapter", nil]
  20. when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil]
  21. when "jdbc" then ["activerecord-jdbc-adapter", nil]
  22. else [database, nil]
  23. end
  24. end
  25. 3 def convert_database_option_for_jruby
  26. if defined?(JRUBY_VERSION)
  27. opt = options.dup
  28. case opt[:database]
  29. when "postgresql" then opt[:database] = "jdbcpostgresql"
  30. when "mysql" then opt[:database] = "jdbcmysql"
  31. when "sqlite3" then opt[:database] = "jdbcsqlite3"
  32. end
  33. self.options = opt.freeze
  34. end
  35. end
  36. 3 private
  37. 3 def mysql_socket
  38. @mysql_socket ||= [
  39. "/tmp/mysql.sock", # default
  40. "/var/run/mysqld/mysqld.sock", # debian/gentoo
  41. "/var/tmp/mysql.sock", # freebsd
  42. "/var/lib/mysql/mysql.sock", # fedora
  43. "/opt/local/lib/mysql/mysql.sock", # fedora
  44. "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
  45. "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
  46. "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
  47. "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
  48. ].find { |f| File.exist?(f) } unless Gem.win_platform?
  49. end
  50. end
  51. end
  52. end

lib/rails/generators/erb.rb

0.0% lines covered

20 relevant lines. 0 lines covered and 20 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/named_base"
  3. module Erb # :nodoc:
  4. module Generators # :nodoc:
  5. class Base < Rails::Generators::NamedBase #:nodoc:
  6. private
  7. def formats
  8. [format]
  9. end
  10. def format
  11. :html
  12. end
  13. def handler
  14. :erb
  15. end
  16. def filename_with_extensions(name, file_format = format)
  17. [name, file_format, handler].compact.join(".")
  18. end
  19. end
  20. end
  21. end

lib/rails/generators/erb/controller/controller_generator.rb

0.0% lines covered

19 relevant lines. 0 lines covered and 19 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/erb"
  3. module Erb # :nodoc:
  4. module Generators # :nodoc:
  5. class ControllerGenerator < Base # :nodoc:
  6. argument :actions, type: :array, default: [], banner: "action action"
  7. def copy_view_files
  8. base_path = File.join("app/views", class_path, file_name)
  9. empty_directory base_path
  10. actions.each do |action|
  11. @action = action
  12. formats.each do |format|
  13. @path = File.join(base_path, filename_with_extensions(action, format))
  14. template filename_with_extensions(:view, format), @path
  15. end
  16. end
  17. end
  18. end
  19. end
  20. end

lib/rails/generators/erb/mailer/mailer_generator.rb

0.0% lines covered

32 relevant lines. 0 lines covered and 32 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/erb"
  3. module Erb # :nodoc:
  4. module Generators # :nodoc:
  5. class MailerGenerator < Base # :nodoc:
  6. argument :actions, type: :array, default: [], banner: "method method"
  7. def copy_view_files
  8. view_base_path = File.join("app/views", class_path, file_name + "_mailer")
  9. empty_directory view_base_path
  10. if behavior == :invoke
  11. formats.each do |format|
  12. layout_path = File.join("app/views/layouts", class_path, filename_with_extensions("mailer", format))
  13. template filename_with_extensions(:layout, format), layout_path unless File.exist?(layout_path)
  14. end
  15. end
  16. actions.each do |action|
  17. @action = action
  18. formats.each do |format|
  19. @path = File.join(view_base_path, filename_with_extensions(action, format))
  20. template filename_with_extensions(:view, format), @path
  21. end
  22. end
  23. end
  24. private
  25. def formats
  26. [:text, :html]
  27. end
  28. def file_name
  29. @_file_name ||= super.sub(/_mailer\z/i, "")
  30. end
  31. end
  32. end
  33. end

lib/rails/generators/erb/scaffold/scaffold_generator.rb

0.0% lines covered

25 relevant lines. 0 lines covered and 25 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/erb"
  3. require "rails/generators/resource_helpers"
  4. module Erb # :nodoc:
  5. module Generators # :nodoc:
  6. class ScaffoldGenerator < Base # :nodoc:
  7. include Rails::Generators::ResourceHelpers
  8. argument :attributes, type: :array, default: [], banner: "field:type field:type"
  9. def create_root_folder
  10. empty_directory File.join("app/views", controller_file_path)
  11. end
  12. def copy_view_files
  13. available_views.each do |view|
  14. formats.each do |format|
  15. filename = filename_with_extensions(view, format)
  16. template filename, File.join("app/views", controller_file_path, filename)
  17. end
  18. end
  19. end
  20. private
  21. def available_views
  22. %w(index edit show new _form)
  23. end
  24. end
  25. end
  26. end

lib/rails/generators/generated_attribute.rb

34.23% lines covered

111 relevant lines. 38 lines covered and 73 lines missed.
    
  1. # frozen_string_literal: true
  2. 13 require "active_support/time"
  3. 13 require "active_support/deprecation"
  4. 13 module Rails
  5. 13 module Generators
  6. 13 class GeneratedAttribute # :nodoc:
  7. 13 INDEX_OPTIONS = %w(index uniq)
  8. 13 UNIQ_INDEX_OPTIONS = %w(uniq)
  9. 13 attr_accessor :name, :type
  10. 13 attr_reader :attr_options
  11. 13 attr_writer :index_name
  12. 13 class << self
  13. 13 def parse(column_definition)
  14. name, type, has_index = column_definition.split(":")
  15. # if user provided "name:index" instead of "name:string:index"
  16. # type should be set blank so GeneratedAttribute's constructor
  17. # could set it to :string
  18. has_index, type = type, nil if INDEX_OPTIONS.include?(type)
  19. type, attr_options = *parse_type_and_options(type)
  20. type = type.to_sym if type
  21. if type && reference?(type)
  22. if UNIQ_INDEX_OPTIONS.include?(has_index)
  23. attr_options[:index] = { unique: true }
  24. end
  25. end
  26. new(name, type, has_index, attr_options)
  27. end
  28. 13 def reference?(type)
  29. [:references, :belongs_to].include? type
  30. end
  31. 13 private
  32. # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
  33. # when declaring options curly brackets should be used
  34. 13 def parse_type_and_options(type)
  35. case type
  36. when /(string|text|binary|integer)\{(\d+)\}/
  37. return $1, limit: $2.to_i
  38. when /decimal\{(\d+)[,.-](\d+)\}/
  39. return :decimal, precision: $1.to_i, scale: $2.to_i
  40. when /(references|belongs_to)\{(.+)\}/
  41. type = $1
  42. provided_options = $2.split(/[,.-]/)
  43. options = Hash[provided_options.map { |opt| [opt.to_sym, true] }]
  44. if options[:required]
  45. ActiveSupport::Deprecation.warn("Passing {required} option has no effect on the model generator. It will be removed in Rails 6.1.\n")
  46. options.delete(:required)
  47. end
  48. return type, options
  49. else
  50. return type, {}
  51. end
  52. end
  53. end
  54. 13 def initialize(name, type = nil, index_type = false, attr_options = {})
  55. @name = name
  56. @type = type || :string
  57. @has_index = INDEX_OPTIONS.include?(index_type)
  58. @has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type)
  59. @attr_options = attr_options
  60. end
  61. 13 def field_type
  62. @field_type ||= case type
  63. when :integer then :number_field
  64. when :float, :decimal then :text_field
  65. when :time then :time_select
  66. when :datetime, :timestamp then :datetime_select
  67. when :date then :date_select
  68. when :text then :text_area
  69. when :rich_text then :rich_text_area
  70. when :boolean then :check_box
  71. when :attachment, :attachments then :file_field
  72. else
  73. :text_field
  74. end
  75. end
  76. 13 def default
  77. @default ||= case type
  78. when :integer then 1
  79. when :float then 1.5
  80. when :decimal then "9.99"
  81. when :datetime, :timestamp, :time then Time.now.to_s(:db)
  82. when :date then Date.today.to_s(:db)
  83. when :string then name == "type" ? "" : "MyString"
  84. when :text then "MyText"
  85. when :boolean then false
  86. when :references, :belongs_to,
  87. :attachment, :attachments,
  88. :rich_text then nil
  89. else
  90. ""
  91. end
  92. end
  93. 13 def plural_name
  94. name.delete_suffix("_id").pluralize
  95. end
  96. 13 def singular_name
  97. name.delete_suffix("_id").singularize
  98. end
  99. 13 def human_name
  100. name.humanize
  101. end
  102. 13 def index_name
  103. @index_name ||= if polymorphic?
  104. %w(id type).map { |t| "#{name}_#{t}" }
  105. else
  106. column_name
  107. end
  108. end
  109. 13 def column_name
  110. @column_name ||= reference? ? "#{name}_id" : name
  111. end
  112. 13 def foreign_key?
  113. name.end_with?("_id")
  114. end
  115. 13 def reference?
  116. self.class.reference?(type)
  117. end
  118. 13 def polymorphic?
  119. attr_options[:polymorphic]
  120. end
  121. 13 def required?
  122. reference? && Rails.application.config.active_record.belongs_to_required_by_default
  123. end
  124. 13 def has_index?
  125. @has_index
  126. end
  127. 13 def has_uniq_index?
  128. @has_uniq_index
  129. end
  130. 13 def password_digest?
  131. name == "password" && type == :digest
  132. end
  133. 13 def token?
  134. type == :token
  135. end
  136. 13 def rich_text?
  137. type == :rich_text
  138. end
  139. 13 def attachment?
  140. type == :attachment
  141. end
  142. 13 def attachments?
  143. type == :attachments
  144. end
  145. 13 def virtual?
  146. rich_text? || attachment? || attachments?
  147. end
  148. 13 def inject_options
  149. (+"").tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
  150. end
  151. 13 def inject_index_options
  152. has_uniq_index? ? ", unique: true" : ""
  153. end
  154. 13 def options_for_migration
  155. @attr_options.dup.tap do |options|
  156. if required?
  157. options[:null] = false
  158. end
  159. if reference? && !polymorphic?
  160. options[:foreign_key] = true
  161. end
  162. end
  163. end
  164. end
  165. end
  166. end

lib/rails/generators/migration.rb

41.67% lines covered

36 relevant lines. 15 lines covered and 21 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/concern"
  3. 1 require "rails/generators/actions/create_migration"
  4. 1 module Rails
  5. 1 module Generators
  6. # Holds common methods for migrations. It assumes that migrations have the
  7. # [0-9]*_name format and can be used by other frameworks (like Sequel)
  8. # just by implementing the next migration version method.
  9. 1 module Migration
  10. 1 extend ActiveSupport::Concern
  11. 1 attr_reader :migration_number, :migration_file_name, :migration_class_name
  12. 1 module ClassMethods #:nodoc:
  13. 1 def migration_lookup_at(dirname)
  14. Dir.glob("#{dirname}/[0-9]*_*.rb")
  15. end
  16. 1 def migration_exists?(dirname, file_name)
  17. migration_lookup_at(dirname).grep(/\d+_#{file_name}.rb$/).first
  18. end
  19. 1 def current_migration_number(dirname)
  20. migration_lookup_at(dirname).collect do |file|
  21. File.basename(file).split("_").first.to_i
  22. end.max.to_i
  23. end
  24. 1 def next_migration_number(dirname)
  25. raise NotImplementedError
  26. end
  27. end
  28. 1 def create_migration(destination, data, config = {}, &block)
  29. action Rails::Generators::Actions::CreateMigration.new(self, destination, block || data.to_s, config)
  30. end
  31. 1 def set_migration_assigns!(destination)
  32. destination = File.expand_path(destination, destination_root)
  33. migration_dir = File.dirname(destination)
  34. @migration_number = self.class.next_migration_number(migration_dir)
  35. @migration_file_name = File.basename(destination, ".rb")
  36. @migration_class_name = @migration_file_name.camelize
  37. end
  38. # Creates a migration template at the given destination. The difference
  39. # to the default template method is that the migration version is appended
  40. # to the destination file name.
  41. #
  42. # The migration version, migration file name, migration class name are
  43. # available as instance variables in the template to be rendered.
  44. #
  45. # migration_template "migration.rb", "db/migrate/add_foo_to_bar.rb"
  46. 1 def migration_template(source, destination, config = {})
  47. source = File.expand_path(find_in_source_paths(source.to_s))
  48. set_migration_assigns!(destination)
  49. context = instance_eval("binding")
  50. dir, base = File.split(destination)
  51. numbered_destination = File.join(dir, ["%migration_number%", base].join("_"))
  52. file = create_migration numbered_destination, nil, config do
  53. if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
  54. ERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer").result(context)
  55. else
  56. ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
  57. end
  58. end
  59. Rails::Generators.add_generated_file(file)
  60. end
  61. end
  62. end
  63. end

lib/rails/generators/model_helpers.rb

50.0% lines covered

20 relevant lines. 10 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. 3 require "rails/generators/active_model"
  3. 3 module Rails
  4. 3 module Generators
  5. 3 module ModelHelpers # :nodoc:
  6. 3 PLURAL_MODEL_NAME_WARN_MESSAGE = "[WARNING] The model name '%s' was recognized as a plural, using the singular '%s' instead. " \
  7. "Override with --force-plural or setup custom inflection rules for this noun before running the generator."
  8. 3 IRREGULAR_MODEL_NAME_WARN_MESSAGE = <<~WARNING
  9. [WARNING] Rails cannot recover singular form from its plural form '%s'.
  10. Please setup custom inflection rules for this noun before running the generator in config/initializers/inflections.rb.
  11. WARNING
  12. 3 mattr_accessor :skip_warn
  13. 3 def self.included(base) #:nodoc:
  14. 4 base.class_option :force_plural, type: :boolean, default: false, desc: "Forces the use of the given model name"
  15. end
  16. 3 def initialize(args, *_options)
  17. super
  18. if name == name.pluralize && name.singularize != name.pluralize && !options[:force_plural]
  19. singular = name.singularize
  20. unless ModelHelpers.skip_warn
  21. say PLURAL_MODEL_NAME_WARN_MESSAGE % [name, singular]
  22. end
  23. name.replace singular
  24. assign_names!(name)
  25. end
  26. if name.singularize != name.pluralize.singularize && ! ModelHelpers.skip_warn
  27. say IRREGULAR_MODEL_NAME_WARN_MESSAGE % [name.pluralize]
  28. end
  29. ModelHelpers.skip_warn = true
  30. end
  31. end
  32. end
  33. end

lib/rails/generators/named_base.rb

42.34% lines covered

111 relevant lines. 47 lines covered and 64 lines missed.
    
  1. # frozen_string_literal: true
  2. 13 require "rails/generators/base"
  3. 13 require "rails/generators/generated_attribute"
  4. 13 module Rails
  5. 13 module Generators
  6. 13 class NamedBase < Base
  7. 13 argument :name, type: :string
  8. 13 def initialize(args, *options) #:nodoc:
  9. @inside_template = nil
  10. # Unfreeze name in case it's given as a frozen string
  11. args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen?
  12. super
  13. assign_names!(name)
  14. parse_attributes! if respond_to?(:attributes)
  15. end
  16. # Overrides <tt>Thor::Actions#template</tt> so it can tell if
  17. # a template is currently being created.
  18. 13 no_tasks do
  19. 13 def template(source, *args, &block)
  20. inside_template do
  21. Rails::Generators.add_generated_file(super)
  22. end
  23. end
  24. 13 def js_template(source, destination)
  25. template(source + ".js", destination + ".js")
  26. end
  27. end
  28. 13 private
  29. 13 attr_reader :file_name
  30. # FIXME: We are avoiding to use alias because a bug on thor that make
  31. # this method public and add it to the task list.
  32. 13 def singular_name # :doc:
  33. file_name
  34. end
  35. 13 def inside_template # :doc:
  36. @inside_template = true
  37. yield
  38. ensure
  39. @inside_template = false
  40. end
  41. 13 def inside_template? # :doc:
  42. @inside_template
  43. end
  44. 13 def file_path # :doc:
  45. @file_path ||= (class_path + [file_name]).join("/")
  46. end
  47. 13 def class_path # :doc:
  48. inside_template? || !namespaced? ? regular_class_path : namespaced_class_path
  49. end
  50. 13 def regular_class_path # :doc:
  51. @class_path
  52. end
  53. 13 def namespaced_class_path # :doc:
  54. @namespaced_class_path ||= namespace_dirs + @class_path
  55. end
  56. 13 def class_name # :doc:
  57. (class_path + [file_name]).map!(&:camelize).join("::")
  58. end
  59. 13 def human_name # :doc:
  60. @human_name ||= singular_name.humanize
  61. end
  62. 13 def plural_name # :doc:
  63. @plural_name ||= singular_name.pluralize
  64. end
  65. 13 def i18n_scope # :doc:
  66. @i18n_scope ||= file_path.tr("/", ".")
  67. end
  68. 13 def table_name # :doc:
  69. @table_name ||= begin
  70. base = pluralize_table_names? ? plural_name : singular_name
  71. (class_path + [base]).join("_")
  72. end
  73. end
  74. 13 def uncountable? # :doc:
  75. singular_name == plural_name
  76. end
  77. 13 def index_helper # :doc:
  78. uncountable? ? "#{plural_route_name}_index" : plural_route_name
  79. end
  80. 13 def show_helper # :doc:
  81. "#{singular_route_name}_url(@#{singular_table_name})"
  82. end
  83. 13 def edit_helper # :doc:
  84. "edit_#{show_helper}"
  85. end
  86. 13 def new_helper # :doc:
  87. "new_#{singular_route_name}_url"
  88. end
  89. 13 def singular_table_name # :doc:
  90. @singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name)
  91. end
  92. 13 def plural_table_name # :doc:
  93. @plural_table_name ||= (pluralize_table_names? ? table_name : table_name.pluralize)
  94. end
  95. 13 def plural_file_name # :doc:
  96. @plural_file_name ||= file_name.pluralize
  97. end
  98. 13 def fixture_file_name # :doc:
  99. @fixture_file_name ||= (pluralize_table_names? ? plural_file_name : file_name)
  100. end
  101. 13 def route_url # :doc:
  102. @route_url ||= class_path.collect { |dname| "/" + dname }.join + "/" + plural_file_name
  103. end
  104. 13 def url_helper_prefix # :doc:
  105. @url_helper_prefix ||= (class_path + [file_name]).join("_")
  106. end
  107. # Tries to retrieve the application name or simply return application.
  108. 13 def application_name # :doc:
  109. if defined?(Rails) && Rails.application
  110. Rails.application.class.name.split("::").first.underscore
  111. else
  112. "application"
  113. end
  114. end
  115. 13 def redirect_resource_name # :doc:
  116. model_resource_name(prefix: "@")
  117. end
  118. 13 def model_resource_name(prefix: "") # :doc:
  119. resource_name = "#{prefix}#{singular_table_name}"
  120. if options[:model_name]
  121. "[#{controller_class_path.map { |name| ":" + name }.join(", ")}, #{resource_name}]"
  122. else
  123. resource_name
  124. end
  125. end
  126. 13 def singular_route_name # :doc:
  127. if options[:model_name]
  128. "#{controller_class_path.join('_')}_#{singular_table_name}"
  129. else
  130. singular_table_name
  131. end
  132. end
  133. 13 def plural_route_name # :doc:
  134. if options[:model_name]
  135. "#{controller_class_path.join('_')}_#{plural_table_name}"
  136. else
  137. plural_table_name
  138. end
  139. end
  140. 13 def assign_names!(name)
  141. @class_path = name.include?("/") ? name.split("/") : name.split("::")
  142. @class_path.map!(&:underscore)
  143. @file_name = @class_path.pop
  144. end
  145. # Convert attributes array into GeneratedAttribute objects.
  146. 13 def parse_attributes!
  147. self.attributes = (attributes || []).map do |attr|
  148. Rails::Generators::GeneratedAttribute.parse(attr)
  149. end
  150. end
  151. 13 def attributes_names # :doc:
  152. @attributes_names ||= attributes.each_with_object([]) do |a, names|
  153. names << a.column_name
  154. names << "password_confirmation" if a.password_digest?
  155. names << "#{a.name}_type" if a.polymorphic?
  156. end
  157. end
  158. 13 def pluralize_table_names? # :doc:
  159. !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names
  160. end
  161. 13 def mountable_engine? # :doc:
  162. defined?(ENGINE_ROOT) && namespaced?
  163. end
  164. # Add a class collisions name to be checked on class initialization. You
  165. # can supply a hash with a :prefix or :suffix to be tested.
  166. #
  167. # ==== Examples
  168. #
  169. # check_class_collision suffix: "Decorator"
  170. #
  171. # If the generator is invoked with class name Admin, it will check for
  172. # the presence of "AdminDecorator".
  173. #
  174. 13 def self.check_class_collision(options = {}) # :doc:
  175. 8 define_method :check_class_collision do
  176. name = if respond_to?(:controller_class_name, true) # for ResourceHelpers
  177. controller_class_name
  178. else
  179. class_name
  180. end
  181. class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}"
  182. end
  183. end
  184. end
  185. end
  186. end

lib/rails/generators/rails/app/app_generator.rb

34.34% lines covered

332 relevant lines. 114 lines covered and 218 lines missed.
    
  1. # frozen_string_literal: true
  2. 3 require "rails/generators/app_base"
  3. 3 module Rails
  4. 3 module ActionMethods # :nodoc:
  5. 3 attr_reader :options
  6. 3 def initialize(generator)
  7. @generator = generator
  8. @options = generator.options
  9. end
  10. 3 private
  11. %w(template copy_file directory empty_directory inside
  12. 3 empty_directory_with_keep_file create_file chmod shebang).each do |method|
  13. 27 class_eval <<-RUBY, __FILE__, __LINE__ + 1
  14. def #{method}(*args, &block)
  15. @generator.send(:#{method}, *args, &block)
  16. end
  17. RUBY
  18. end
  19. 3 def method_missing(meth, *args, &block)
  20. @generator.send(meth, *args, &block)
  21. end
  22. end
  23. # The application builder allows you to override elements of the application
  24. # generator without being forced to reverse the operations of the default
  25. # generator.
  26. #
  27. # This allows you to override entire operations, like the creation of the
  28. # Gemfile, README, or JavaScript files, without needing to know exactly
  29. # what those operations do so you can create another template action.
  30. #
  31. # class CustomAppBuilder < Rails::AppBuilder
  32. # def test
  33. # @generator.gem "rspec-rails", group: [:development, :test]
  34. # run "bundle install"
  35. # generate "rspec:install"
  36. # end
  37. # end
  38. 3 class AppBuilder
  39. 3 def rakefile
  40. template "Rakefile"
  41. end
  42. 3 def readme
  43. copy_file "README.md", "README.md"
  44. end
  45. 3 def ruby_version
  46. template "ruby-version", ".ruby-version"
  47. end
  48. 3 def gemfile
  49. template "Gemfile"
  50. end
  51. 3 def configru
  52. template "config.ru"
  53. end
  54. 3 def gitignore
  55. template "gitignore", ".gitignore"
  56. end
  57. 3 def version_control
  58. if !options[:skip_git] && !options[:pretend]
  59. run "git init", capture: options[:quiet], abort_on_failure: false
  60. end
  61. end
  62. 3 def package_json
  63. template "package.json"
  64. end
  65. 3 def app
  66. directory "app"
  67. empty_directory_with_keep_file "app/assets/images"
  68. keep_file "app/controllers/concerns"
  69. keep_file "app/models/concerns"
  70. end
  71. 3 def bin
  72. directory "bin" do |content|
  73. "#{shebang}\n" + content
  74. end
  75. chmod "bin", 0755 & ~File.umask, verbose: false
  76. end
  77. 3 def bin_when_updating
  78. bin
  79. if options[:skip_javascript]
  80. remove_file "bin/yarn"
  81. end
  82. end
  83. 3 def yarn_when_updating
  84. return if File.exist?("bin/yarn")
  85. template "bin/yarn" do |content|
  86. "#{shebang}\n" + content
  87. end
  88. chmod "bin", 0755 & ~File.umask, verbose: false
  89. end
  90. 3 def config
  91. empty_directory "config"
  92. inside "config" do
  93. template "routes.rb"
  94. template "application.rb"
  95. template "environment.rb"
  96. template "cable.yml" unless options[:skip_action_cable]
  97. template "puma.rb" unless options[:skip_puma]
  98. template "spring.rb" if spring_install?
  99. template "storage.yml" unless skip_active_storage?
  100. directory "environments"
  101. directory "initializers"
  102. directory "locales"
  103. end
  104. end
  105. 3 def config_when_updating
  106. cookie_serializer_config_exist = File.exist?("config/initializers/cookies_serializer.rb")
  107. action_cable_config_exist = File.exist?("config/cable.yml")
  108. active_storage_config_exist = File.exist?("config/storage.yml")
  109. rack_cors_config_exist = File.exist?("config/initializers/cors.rb")
  110. assets_config_exist = File.exist?("config/initializers/assets.rb")
  111. csp_config_exist = File.exist?("config/initializers/content_security_policy.rb")
  112. feature_policy_config_exist = File.exist?("config/initializers/feature_policy.rb")
  113. @config_target_version = Rails.application.config.loaded_config_version || "5.0"
  114. config
  115. unless cookie_serializer_config_exist
  116. gsub_file "config/initializers/cookies_serializer.rb", /json(?!,)/, "marshal"
  117. end
  118. if !options[:skip_action_cable] && !action_cable_config_exist
  119. template "config/cable.yml"
  120. end
  121. if !skip_active_storage? && !active_storage_config_exist
  122. template "config/storage.yml"
  123. end
  124. if options[:skip_sprockets] && !assets_config_exist
  125. remove_file "config/initializers/assets.rb"
  126. end
  127. unless rack_cors_config_exist
  128. remove_file "config/initializers/cors.rb"
  129. end
  130. if options[:api]
  131. unless cookie_serializer_config_exist
  132. remove_file "config/initializers/cookies_serializer.rb"
  133. end
  134. unless csp_config_exist
  135. remove_file "config/initializers/content_security_policy.rb"
  136. end
  137. unless feature_policy_config_exist
  138. remove_file "config/initializers/feature_policy.rb"
  139. end
  140. end
  141. end
  142. 3 def master_key
  143. return if options[:pretend] || options[:dummy_app]
  144. require "rails/generators/rails/master_key/master_key_generator"
  145. master_key_generator = Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet], force: options[:force])
  146. master_key_generator.add_master_key_file_silently
  147. master_key_generator.ignore_master_key_file_silently
  148. end
  149. 3 def credentials
  150. return if options[:pretend] || options[:dummy_app]
  151. require "rails/generators/rails/credentials/credentials_generator"
  152. Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently
  153. end
  154. 3 def database_yml
  155. template "config/databases/#{options[:database]}.yml", "config/database.yml"
  156. end
  157. 3 def db
  158. directory "db"
  159. end
  160. 3 def lib
  161. empty_directory "lib"
  162. empty_directory_with_keep_file "lib/tasks"
  163. empty_directory_with_keep_file "lib/assets"
  164. end
  165. 3 def log
  166. empty_directory_with_keep_file "log"
  167. end
  168. 3 def public_directory
  169. directory "public", "public", recursive: false
  170. end
  171. 3 def storage
  172. empty_directory_with_keep_file "storage"
  173. empty_directory_with_keep_file "tmp/storage"
  174. end
  175. 3 def test
  176. empty_directory_with_keep_file "test/fixtures/files"
  177. empty_directory_with_keep_file "test/controllers"
  178. empty_directory_with_keep_file "test/mailers"
  179. empty_directory_with_keep_file "test/models"
  180. empty_directory_with_keep_file "test/helpers"
  181. empty_directory_with_keep_file "test/integration"
  182. template "test/channels/application_cable/connection_test.rb"
  183. template "test/test_helper.rb"
  184. end
  185. 3 def system_test
  186. empty_directory_with_keep_file "test/system"
  187. template "test/application_system_test_case.rb"
  188. end
  189. 3 def tmp
  190. empty_directory_with_keep_file "tmp"
  191. empty_directory_with_keep_file "tmp/pids"
  192. empty_directory "tmp/cache"
  193. empty_directory "tmp/cache/assets"
  194. end
  195. 3 def vendor
  196. empty_directory_with_keep_file "vendor"
  197. end
  198. 3 def config_target_version
  199. defined?(@config_target_version) ? @config_target_version : Rails::VERSION::STRING.to_f
  200. end
  201. end
  202. 3 module Generators
  203. # We need to store the RAILS_DEV_PATH in a constant, otherwise the path
  204. # can change in Ruby 1.8.7 when we FileUtils.cd.
  205. 3 RAILS_DEV_PATH = File.expand_path("../../../../../..", __dir__)
  206. 3 class AppGenerator < AppBase
  207. # :stopdoc:
  208. 3 WEBPACKS = %w( react vue angular elm stimulus )
  209. 3 add_shared_options_for "application"
  210. # Add rails command options
  211. 3 class_option :version, type: :boolean, aliases: "-v", group: :rails,
  212. desc: "Show Rails version number and quit"
  213. 3 class_option :api, type: :boolean,
  214. desc: "Preconfigure smaller stack for API only apps"
  215. 3 class_option :minimal, type: :boolean,
  216. desc: "Preconfigure a minimal rails app"
  217. 3 class_option :skip_bundle, type: :boolean, aliases: "-B", default: false,
  218. desc: "Don't run bundle install"
  219. 3 class_option :webpack, type: :string, aliases: "--webpacker", default: nil,
  220. desc: "Preconfigure Webpack with a particular framework (options: #{WEBPACKS.join(", ")})"
  221. 3 class_option :skip_webpack_install, type: :boolean, default: false,
  222. desc: "Don't run Webpack install"
  223. 3 def initialize(*args)
  224. super
  225. if !options[:skip_active_record] && !DATABASES.include?(options[:database])
  226. raise Error, "Invalid value for --database option. Supported preconfigurations are: #{DATABASES.join(", ")}."
  227. end
  228. # Force sprockets and yarn to be skipped when generating API only apps.
  229. # Can't modify options hash as it's frozen by default.
  230. if options[:api]
  231. self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze
  232. end
  233. if options[:minimal]
  234. self.options = options.merge(
  235. skip_action_cable: true,
  236. skip_action_mailer: true,
  237. skip_action_mailbox: true,
  238. skip_action_text: true,
  239. skip_active_job: true,
  240. skip_active_storage: true,
  241. skip_bootsnap: true,
  242. skip_dev_gems: true,
  243. skip_javascript: true,
  244. skip_jbuilder: true,
  245. skip_spring: true,
  246. skip_system_test: true,
  247. skip_webpack_install: true,
  248. skip_turbolinks: true).tap do |option|
  249. if option[:webpack]
  250. option[:skip_webpack_install] = false
  251. option[:skip_javascript] = false
  252. end
  253. end.freeze
  254. end
  255. @after_bundle_callbacks = []
  256. end
  257. 3 public_task :set_default_accessors!
  258. 3 public_task :create_root
  259. 3 def create_root_files
  260. build(:readme)
  261. build(:rakefile)
  262. build(:ruby_version)
  263. build(:configru)
  264. build(:gitignore) unless options[:skip_git]
  265. build(:gemfile) unless options[:skip_gemfile]
  266. build(:version_control)
  267. build(:package_json) unless options[:skip_javascript]
  268. end
  269. 3 def create_app_files
  270. build(:app)
  271. end
  272. 3 def create_bin_files
  273. build(:bin)
  274. end
  275. 3 def update_bin_files
  276. build(:bin_when_updating)
  277. end
  278. 3 remove_task :update_bin_files
  279. 3 def update_bin_yarn
  280. build(:yarn_when_updating)
  281. end
  282. 3 remove_task :update_bin_yarn
  283. 3 def update_active_storage
  284. unless skip_active_storage?
  285. rails_command "active_storage:update", inline: true
  286. end
  287. end
  288. 3 remove_task :update_active_storage
  289. 3 def create_config_files
  290. build(:config)
  291. end
  292. 3 def update_config_files
  293. build(:config_when_updating)
  294. end
  295. 3 remove_task :update_config_files
  296. 3 def create_master_key
  297. build(:master_key)
  298. end
  299. 3 def create_credentials
  300. build(:credentials)
  301. end
  302. 3 def display_upgrade_guide_info
  303. say "\nAfter this, check Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html for more details about upgrading your app."
  304. end
  305. 3 remove_task :display_upgrade_guide_info
  306. 3 def create_boot_file
  307. template "config/boot.rb"
  308. end
  309. 3 def create_active_record_files
  310. return if options[:skip_active_record]
  311. build(:database_yml)
  312. end
  313. 3 def create_db_files
  314. return if options[:skip_active_record]
  315. build(:db)
  316. end
  317. 3 def create_lib_files
  318. build(:lib)
  319. end
  320. 3 def create_log_files
  321. build(:log)
  322. end
  323. 3 def create_public_files
  324. build(:public_directory)
  325. end
  326. 3 def create_tmp_files
  327. build(:tmp)
  328. end
  329. 3 def create_vendor_files
  330. build(:vendor)
  331. end
  332. 3 def create_test_files
  333. build(:test) unless options[:skip_test]
  334. end
  335. 3 def create_system_test_files
  336. build(:system_test) if depends_on_system_test?
  337. end
  338. 3 def create_storage_files
  339. build(:storage) unless skip_active_storage?
  340. end
  341. 3 def delete_app_assets_if_api_option
  342. if options[:api]
  343. remove_dir "app/assets"
  344. remove_dir "lib/assets"
  345. remove_dir "tmp/cache/assets"
  346. end
  347. end
  348. 3 def delete_app_helpers_if_api_option
  349. if options[:api]
  350. remove_dir "app/helpers"
  351. remove_dir "test/helpers"
  352. end
  353. end
  354. 3 def delete_app_views_if_api_option
  355. if options[:api]
  356. if options[:skip_action_mailer]
  357. remove_dir "app/views"
  358. else
  359. remove_file "app/views/layouts/application.html.erb"
  360. end
  361. end
  362. end
  363. 3 def delete_public_files_if_api_option
  364. if options[:api]
  365. remove_file "public/404.html"
  366. remove_file "public/422.html"
  367. remove_file "public/500.html"
  368. remove_file "public/apple-touch-icon-precomposed.png"
  369. remove_file "public/apple-touch-icon.png"
  370. remove_file "public/favicon.ico"
  371. end
  372. end
  373. 3 def delete_js_folder_skipping_javascript
  374. if options[:skip_javascript] && !options[:minimal]
  375. remove_dir "app/javascript"
  376. end
  377. end
  378. 3 def delete_js_packs_when_minimal_skipping_webpack
  379. if options[:minimal] && options[:skip_webpack_install]
  380. remove_dir "app/javascript/packs"
  381. keep_file "app/javascript"
  382. end
  383. end
  384. 3 def delete_assets_initializer_skipping_sprockets
  385. if options[:skip_sprockets]
  386. remove_file "config/initializers/assets.rb"
  387. end
  388. end
  389. 3 def delete_application_record_skipping_active_record
  390. if options[:skip_active_record]
  391. remove_file "app/models/application_record.rb"
  392. end
  393. end
  394. 3 def delete_active_job_folder_if_skipping_active_job
  395. if options[:skip_active_job]
  396. remove_dir "app/jobs"
  397. end
  398. end
  399. 3 def delete_action_mailer_files_skipping_action_mailer
  400. if options[:skip_action_mailer]
  401. remove_file "app/views/layouts/mailer.html.erb"
  402. remove_file "app/views/layouts/mailer.text.erb"
  403. remove_dir "app/mailers"
  404. remove_dir "test/mailers"
  405. end
  406. end
  407. 3 def delete_action_cable_files_skipping_action_cable
  408. if options[:skip_action_cable]
  409. remove_dir "app/javascript/channels"
  410. remove_dir "app/channels"
  411. remove_dir "test/channels"
  412. end
  413. end
  414. 3 def delete_non_api_initializers_if_api_option
  415. if options[:api]
  416. remove_file "config/initializers/cookies_serializer.rb"
  417. remove_file "config/initializers/content_security_policy.rb"
  418. remove_file "config/initializers/feature_policy.rb"
  419. end
  420. end
  421. 3 def delete_api_initializers
  422. unless options[:api]
  423. remove_file "config/initializers/cors.rb"
  424. end
  425. end
  426. 3 def delete_new_framework_defaults
  427. unless options[:update]
  428. remove_file "config/initializers/new_framework_defaults_6_1.rb"
  429. end
  430. end
  431. 3 def delete_bin_yarn
  432. remove_file "bin/yarn" if options[:skip_javascript]
  433. end
  434. 3 def finish_template
  435. build(:leftovers)
  436. end
  437. 3 public_task :apply_rails_template, :run_bundle
  438. 3 public_task :generate_bundler_binstub, :generate_spring_binstub
  439. 3 public_task :run_webpack
  440. 3 def run_after_bundle_callbacks
  441. @after_bundle_callbacks.each(&:call)
  442. end
  443. 3 def self.banner
  444. "rails new #{arguments.map(&:usage).join(' ')} [options]"
  445. end
  446. # :startdoc:
  447. 3 private
  448. # Define file as an alias to create_file for backwards compatibility.
  449. 3 def file(*args, &block)
  450. create_file(*args, &block)
  451. end
  452. # Registers a callback to be executed after bundle and spring binstubs
  453. # have run.
  454. #
  455. # after_bundle do
  456. # git add: '.'
  457. # end
  458. 3 def after_bundle(&block) # :doc:
  459. @after_bundle_callbacks << block
  460. end
  461. 3 def get_builder_class
  462. defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder
  463. end
  464. end
  465. # This class handles preparation of the arguments before the AppGenerator is
  466. # called. The class provides version or help information if they were
  467. # requested, and also constructs the railsrc file (used for extra configuration
  468. # options).
  469. #
  470. # This class should be called before the AppGenerator is required and started
  471. # since it configures and mutates ARGV correctly.
  472. 3 class ARGVScrubber # :nodoc:
  473. 3 def initialize(argv = ARGV)
  474. @argv = argv
  475. end
  476. 3 def prepare!
  477. handle_version_request!(@argv.first)
  478. handle_invalid_command!(@argv.first, @argv) do
  479. handle_rails_rc!(@argv.drop(1))
  480. end
  481. end
  482. 3 def self.default_rc_file
  483. File.expand_path("~/.railsrc")
  484. end
  485. 3 private
  486. 3 def handle_version_request!(argument)
  487. if ["--version", "-v"].include?(argument)
  488. require "rails/version"
  489. puts "Rails #{Rails::VERSION::STRING}"
  490. exit(0)
  491. end
  492. end
  493. 3 def handle_invalid_command!(argument, argv)
  494. if argument == "new"
  495. yield
  496. else
  497. ["--help"] + argv.drop(1)
  498. end
  499. end
  500. 3 def handle_rails_rc!(argv)
  501. if argv.find { |arg| arg == "--no-rc" }
  502. argv.reject { |arg| arg == "--no-rc" }
  503. else
  504. railsrc(argv) { |rc_argv, rc| insert_railsrc_into_argv!(rc_argv, rc) }
  505. end
  506. end
  507. 3 def railsrc(argv)
  508. if (customrc = argv.index { |x| x.include?("--rc=") })
  509. fname = File.expand_path(argv[customrc].gsub(/--rc=/, ""))
  510. yield(argv.take(customrc) + argv.drop(customrc + 1), fname)
  511. else
  512. yield argv, self.class.default_rc_file
  513. end
  514. end
  515. 3 def read_rc_file(railsrc)
  516. extra_args = File.readlines(railsrc).flat_map(&:split)
  517. puts "Using #{extra_args.join(" ")} from #{railsrc}"
  518. extra_args
  519. end
  520. 3 def insert_railsrc_into_argv!(argv, railsrc)
  521. return argv unless File.exist?(railsrc)
  522. extra_args = read_rc_file railsrc
  523. argv.take(1) + extra_args + argv.drop(1)
  524. end
  525. end
  526. end
  527. end

lib/rails/generators/rails/application_record/application_record_generator.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rails
  3. 1 module Generators
  4. 1 class ApplicationRecordGenerator < Base # :nodoc:
  5. 1 hook_for :orm, required: true, desc: "ORM to be invoked"
  6. end
  7. end
  8. end

lib/rails/generators/rails/assets/assets_generator.rb

0.0% lines covered

20 relevant lines. 0 lines covered and 20 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Generators
  4. class AssetsGenerator < NamedBase # :nodoc:
  5. class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
  6. class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
  7. class_option :javascript_engine, desc: "Engine for JavaScripts"
  8. class_option :stylesheet_engine, desc: "Engine for Stylesheets"
  9. private
  10. def asset_name
  11. file_name
  12. end
  13. hook_for :javascript_engine do |javascript_engine|
  14. invoke javascript_engine, [name] if options[:javascripts]
  15. end
  16. hook_for :stylesheet_engine do |stylesheet_engine|
  17. invoke stylesheet_engine, [name] if options[:stylesheets]
  18. end
  19. end
  20. end
  21. end

lib/rails/generators/rails/benchmark/benchmark_generator.rb

0.0% lines covered

22 relevant lines. 0 lines covered and 22 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/named_base"
  3. module Rails
  4. module Generators
  5. class BenchmarkGenerator < NamedBase
  6. IPS_GEM_NAME = "benchmark-ips"
  7. argument :reports, type: :array, default: ["before", "after"]
  8. def generate_layout
  9. add_ips_to_gemfile unless ips_installed?
  10. template("benchmark.rb.tt", "script/benchmarks/#{file_name}.rb")
  11. end
  12. private
  13. def add_ips_to_gemfile
  14. gem(IPS_GEM_NAME, group: [:development, :test])
  15. end
  16. def ips_installed?
  17. in_root do
  18. return File.read("Gemfile").match?(/gem.*\b#{IPS_GEM_NAME}\b.*/)
  19. end
  20. end
  21. end
  22. end
  23. end

lib/rails/generators/rails/controller/controller_generator.rb

63.64% lines covered

22 relevant lines. 14 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. 2 module Rails
  3. 2 module Generators
  4. 2 class ControllerGenerator < NamedBase # :nodoc:
  5. 2 argument :actions, type: :array, default: [], banner: "action action"
  6. 2 class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
  7. 2 class_option :helper, type: :boolean
  8. 2 class_option :assets, type: :boolean
  9. 2 check_class_collision suffix: "Controller"
  10. 2 def create_controller_files
  11. template "controller.rb", File.join("app/controllers", class_path, "#{file_name}_controller.rb")
  12. end
  13. 2 def add_routes
  14. return if options[:skip_routes]
  15. return if actions.empty?
  16. routing_code = actions.map { |action| "get '#{file_name}/#{action}'" }.join("\n")
  17. route routing_code, namespace: regular_class_path
  18. end
  19. 2 hook_for :template_engine, :test_framework, :helper, :assets do |generator|
  20. invoke generator, [ remove_possible_suffix(name), actions ]
  21. end
  22. 2 private
  23. 2 def file_name
  24. @_file_name ||= remove_possible_suffix(super)
  25. end
  26. 2 def remove_possible_suffix(name)
  27. name.sub(/_?controller$/i, "")
  28. end
  29. end
  30. end
  31. end

lib/rails/generators/rails/credentials/credentials_generator.rb

0.0% lines covered

42 relevant lines. 0 lines covered and 42 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/base"
  3. require "rails/generators/rails/master_key/master_key_generator"
  4. require "active_support/encrypted_configuration"
  5. module Rails
  6. module Generators
  7. class CredentialsGenerator < Base # :nodoc:
  8. def add_credentials_file
  9. unless credentials.content_path.exist?
  10. template = credentials_template
  11. say "Adding #{credentials.content_path} to store encrypted credentials."
  12. say ""
  13. say "The following content has been encrypted with the Rails master key:"
  14. say ""
  15. say template, :on_green
  16. say ""
  17. add_credentials_file_silently(template)
  18. say "You can edit encrypted credentials with `bin/rails credentials:edit`."
  19. say ""
  20. end
  21. end
  22. def add_credentials_file_silently(template = nil)
  23. unless credentials.content_path.exist?
  24. credentials.write(credentials_template)
  25. end
  26. end
  27. private
  28. def credentials
  29. ActiveSupport::EncryptedConfiguration.new(
  30. config_path: "config/credentials.yml.enc",
  31. key_path: "config/master.key",
  32. env_key: "RAILS_MASTER_KEY",
  33. raise_if_missing_key: true
  34. )
  35. end
  36. def credentials_template
  37. <<~YAML
  38. # aws:
  39. # access_key_id: 123
  40. # secret_access_key: 345
  41. # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
  42. secret_key_base: #{SecureRandom.hex(64)}
  43. YAML
  44. end
  45. end
  46. end
  47. end

lib/rails/generators/rails/db/system/change/change_generator.rb

0.0% lines covered

51 relevant lines. 0 lines covered and 51 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/base"
  3. module Rails
  4. module Generators
  5. module Db
  6. module System
  7. class ChangeGenerator < Base # :nodoc:
  8. include Database
  9. include AppName
  10. class_option :to, required: true,
  11. desc: "The database system to switch to."
  12. def self.default_generator_root
  13. path = File.expand_path(File.join(base_name, "app"), base_root)
  14. path if File.exist?(path)
  15. end
  16. def initialize(*)
  17. super
  18. unless DATABASES.include?(options[:to])
  19. raise Error, "Invalid value for --to option. Supported preconfigurations are: #{DATABASES.join(", ")}."
  20. end
  21. opt = options.dup
  22. opt[:database] ||= opt[:to]
  23. self.options = opt.freeze
  24. end
  25. def edit_database_config
  26. template("config/databases/#{options[:database]}.yml", "config/database.yml")
  27. end
  28. def edit_gemfile
  29. name, version = gem_for_database
  30. gsub_file("Gemfile", all_database_gems_regex, name)
  31. gsub_file("Gemfile", gem_entry_regex_for(name), gem_entry_for(name, *version))
  32. end
  33. private
  34. def all_database_gems
  35. DATABASES.map { |database| gem_for_database(database) }
  36. end
  37. def all_database_gems_regex
  38. all_database_gem_names = all_database_gems.map(&:first)
  39. /(\b#{all_database_gem_names.join('\b|\b')}\b)/
  40. end
  41. def gem_entry_regex_for(gem_name)
  42. /^gem.*\b#{gem_name}\b.*/
  43. end
  44. def gem_entry_for(*gem_name_and_version)
  45. gem_name_and_version.map! { |segment| "'#{segment}'" }
  46. "gem #{gem_name_and_version.join(", ")}"
  47. end
  48. end
  49. end
  50. end
  51. end
  52. end

lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb

0.0% lines covered

23 relevant lines. 0 lines covered and 23 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/base"
  3. require "active_support/encrypted_file"
  4. module Rails
  5. module Generators
  6. class EncryptedFileGenerator < Base # :nodoc:
  7. def add_encrypted_file_silently(file_path, key_path, template = encrypted_file_template)
  8. unless File.exist?(file_path)
  9. ActiveSupport::EncryptedFile.new(
  10. content_path: file_path,
  11. key_path: key_path,
  12. env_key: "RAILS_MASTER_KEY",
  13. raise_if_missing_key: true
  14. ).write(template)
  15. end
  16. end
  17. private
  18. def encrypted_file_template
  19. <<~YAML
  20. # aws:
  21. # access_key_id: 123
  22. # secret_access_key: 345
  23. YAML
  24. end
  25. end
  26. end
  27. end

lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb

0.0% lines covered

48 relevant lines. 0 lines covered and 48 lines missed.
    
  1. # frozen_string_literal: true
  2. require "pathname"
  3. require "rails/generators/base"
  4. require "active_support/encrypted_file"
  5. module Rails
  6. module Generators
  7. class EncryptionKeyFileGenerator < Base # :nodoc:
  8. def add_key_file(key_path)
  9. key_path = Pathname.new(key_path)
  10. unless key_path.exist?
  11. key = ActiveSupport::EncryptedFile.generate_key
  12. log "Adding #{key_path} to store the encryption key: #{key}"
  13. log ""
  14. log "Save this in a password manager your team can access."
  15. log ""
  16. log "If you lose the key, no one, including you, can access anything encrypted with it."
  17. log ""
  18. add_key_file_silently(key_path, key)
  19. log ""
  20. end
  21. end
  22. def add_key_file_silently(key_path, key = nil)
  23. create_file key_path, key || ActiveSupport::EncryptedFile.generate_key
  24. key_path.chmod 0600
  25. end
  26. def ignore_key_file(key_path, ignore: key_ignore(key_path))
  27. if File.exist?(".gitignore")
  28. unless File.read(".gitignore").include?(ignore)
  29. log "Ignoring #{key_path} so it won't end up in Git history:"
  30. log ""
  31. append_to_file ".gitignore", ignore
  32. log ""
  33. end
  34. else
  35. log "IMPORTANT: Don't commit #{key_path}. Add this to your ignore file:"
  36. log ignore, :on_green
  37. log ""
  38. end
  39. end
  40. def ignore_key_file_silently(key_path, ignore: key_ignore(key_path))
  41. append_to_file ".gitignore", ignore if File.exist?(".gitignore")
  42. end
  43. private
  44. def key_ignore(key_path)
  45. [ "", "/#{key_path}", "" ].join("\n")
  46. end
  47. end
  48. end
  49. end

lib/rails/generators/rails/generator/generator_generator.rb

69.23% lines covered

13 relevant lines. 9 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rails
  3. 1 module Generators
  4. 1 class GeneratorGenerator < NamedBase # :nodoc:
  5. 1 check_class_collision suffix: "Generator"
  6. 1 class_option :namespace, type: :boolean, default: true,
  7. desc: "Namespace generator under lib/generators/name"
  8. 1 def create_generator_files
  9. directory ".", generator_dir
  10. end
  11. 1 hook_for :test_framework
  12. 1 private
  13. 1 def generator_dir
  14. if options[:namespace]
  15. File.join("lib", "generators", regular_class_path, file_name)
  16. else
  17. File.join("lib", "generators", regular_class_path)
  18. end
  19. end
  20. end
  21. end
  22. end

lib/rails/generators/rails/helper/helper_generator.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rails
  3. 1 module Generators
  4. 1 class HelperGenerator < NamedBase # :nodoc:
  5. 1 check_class_collision suffix: "Helper"
  6. 1 def create_helper_files
  7. template "helper.rb", File.join("app/helpers", class_path, "#{file_name}_helper.rb")
  8. end
  9. 1 hook_for :test_framework
  10. 1 private
  11. 1 def file_name
  12. @_file_name ||= super.sub(/_helper\z/i, "")
  13. end
  14. end
  15. end
  16. end

lib/rails/generators/rails/integration_test/integration_test_generator.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rails
  3. 1 module Generators
  4. 1 class IntegrationTestGenerator < NamedBase # :nodoc:
  5. 1 hook_for :integration_tool, as: :integration
  6. end
  7. end
  8. end

lib/rails/generators/rails/master_key/master_key_generator.rb

0.0% lines covered

42 relevant lines. 0 lines covered and 42 lines missed.
    
  1. # frozen_string_literal: true
  2. require "pathname"
  3. require "rails/generators/base"
  4. require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
  5. require "active_support/encrypted_file"
  6. module Rails
  7. module Generators
  8. class MasterKeyGenerator < Base # :nodoc:
  9. MASTER_KEY_PATH = Pathname.new("config/master.key")
  10. def add_master_key_file
  11. unless MASTER_KEY_PATH.exist?
  12. key = ActiveSupport::EncryptedFile.generate_key
  13. log "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}"
  14. log ""
  15. log "Save this in a password manager your team can access."
  16. log ""
  17. log "If you lose the key, no one, including you, can access anything encrypted with it."
  18. log ""
  19. add_master_key_file_silently(key)
  20. log ""
  21. end
  22. end
  23. def add_master_key_file_silently(key = nil)
  24. unless MASTER_KEY_PATH.exist?
  25. key_file_generator.add_key_file_silently(MASTER_KEY_PATH, key)
  26. end
  27. end
  28. def ignore_master_key_file
  29. key_file_generator.ignore_key_file(MASTER_KEY_PATH, ignore: key_ignore)
  30. end
  31. def ignore_master_key_file_silently
  32. key_file_generator.ignore_key_file_silently(MASTER_KEY_PATH, ignore: key_ignore)
  33. end
  34. private
  35. def key_file_generator
  36. EncryptionKeyFileGenerator.new([], options)
  37. end
  38. def key_ignore
  39. [ "", "# Ignore master key for decrypting credentials and more.", "/#{MASTER_KEY_PATH}", "" ].join("\n")
  40. end
  41. end
  42. end
  43. end

lib/rails/generators/rails/migration/migration_generator.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 2 module Rails
  3. 2 module Generators
  4. 2 class MigrationGenerator < NamedBase # :nodoc:
  5. 2 argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
  6. 2 hook_for :orm, required: true, desc: "ORM to be invoked"
  7. end
  8. end
  9. end

lib/rails/generators/rails/model/model_generator.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 2 require "rails/generators/model_helpers"
  3. 2 module Rails
  4. 2 module Generators
  5. 2 class ModelGenerator < NamedBase # :nodoc:
  6. 2 include Rails::Generators::ModelHelpers
  7. 2 argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
  8. 2 hook_for :orm, required: true, desc: "ORM to be invoked"
  9. end
  10. end
  11. end

lib/rails/generators/rails/plugin/plugin_generator.rb

33.48% lines covered

233 relevant lines. 78 lines covered and 155 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "active_support/core_ext/hash/except"
  3. 1 require "rails/generators/rails/app/app_generator"
  4. 1 require "date"
  5. 1 module Rails
  6. # The plugin builder allows you to override elements of the plugin
  7. # generator without being forced to reverse the operations of the default
  8. # generator.
  9. #
  10. # This allows you to override entire operations, like the creation of the
  11. # Gemfile, \README, or JavaScript files, without needing to know exactly
  12. # what those operations do so you can create another template action.
  13. 1 class PluginBuilder
  14. 1 def rakefile
  15. template "Rakefile"
  16. end
  17. 1 def app
  18. if mountable?
  19. if api?
  20. directory "app", exclude_pattern: %r{app/(views|helpers)}
  21. else
  22. directory "app"
  23. empty_directory_with_keep_file "app/assets/images/#{namespaced_name}"
  24. end
  25. remove_dir "app/mailers" if options[:skip_action_mailer]
  26. remove_dir "app/jobs" if options[:skip_active_job]
  27. elsif full?
  28. empty_directory_with_keep_file "app/models"
  29. empty_directory_with_keep_file "app/controllers"
  30. empty_directory_with_keep_file "app/mailers" unless options[:skip_action_mailer]
  31. empty_directory_with_keep_file "app/jobs" unless options[:skip_active_job]
  32. unless api?
  33. empty_directory_with_keep_file "app/assets/images/#{namespaced_name}"
  34. empty_directory_with_keep_file "app/helpers"
  35. empty_directory_with_keep_file "app/views"
  36. end
  37. end
  38. end
  39. 1 def readme
  40. template "README.md"
  41. end
  42. 1 def gemfile
  43. template "Gemfile"
  44. end
  45. 1 def license
  46. template "MIT-LICENSE"
  47. end
  48. 1 def gemspec
  49. template "%name%.gemspec"
  50. end
  51. 1 def gitignore
  52. template "gitignore", ".gitignore"
  53. end
  54. 1 def version_control
  55. if !options[:skip_git] && !options[:pretend]
  56. run "git init", capture: options[:quiet], abort_on_failure: false
  57. end
  58. end
  59. 1 def lib
  60. template "lib/%namespaced_name%.rb"
  61. template "lib/tasks/%namespaced_name%_tasks.rake"
  62. template "lib/%namespaced_name%/version.rb"
  63. if engine?
  64. template "lib/%namespaced_name%/engine.rb"
  65. else
  66. template "lib/%namespaced_name%/railtie.rb"
  67. end
  68. end
  69. 1 def config
  70. template "config/routes.rb" if engine?
  71. end
  72. 1 def test
  73. template "test/test_helper.rb"
  74. template "test/%namespaced_name%_test.rb"
  75. append_file "Rakefile", <<-EOF
  76. #{rakefile_test_tasks}
  77. task default: :test
  78. EOF
  79. if engine?
  80. template "test/integration/navigation_test.rb"
  81. end
  82. end
  83. 1 DUMMY_IGNORE_OPTIONS = %i[dev edge master template]
  84. 1 def generate_test_dummy(force = false)
  85. opts = options.transform_keys(&:to_sym).except(*DUMMY_IGNORE_OPTIONS)
  86. opts[:force] = force
  87. opts[:skip_bundle] = true
  88. opts[:skip_listen] = true
  89. opts[:skip_git] = true
  90. opts[:skip_turbolinks] = true
  91. opts[:skip_webpack_install] = true
  92. opts[:dummy_app] = true
  93. invoke Rails::Generators::AppGenerator,
  94. [ File.expand_path(dummy_path, destination_root) ], opts
  95. end
  96. 1 def test_dummy_config
  97. template "rails/boot.rb", "#{dummy_path}/config/boot.rb", force: true
  98. insert_into_file "#{dummy_path}/config/application.rb", <<~RUBY, after: /^Bundler\.require.+\n/
  99. require #{namespaced_name.inspect}
  100. RUBY
  101. if mountable?
  102. template "rails/routes.rb", "#{dummy_path}/config/routes.rb", force: true
  103. end
  104. end
  105. 1 def test_dummy_assets
  106. template "rails/javascripts.js", "#{dummy_path}/app/javascript/packs/application.js", force: true
  107. template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
  108. template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/config/manifest.js", force: true
  109. end
  110. 1 def test_dummy_clean
  111. inside dummy_path do
  112. remove_file ".ruby-version"
  113. remove_file "db/seeds.rb"
  114. remove_file "Gemfile"
  115. remove_file "lib/tasks"
  116. remove_file "public/robots.txt"
  117. remove_file "README.md"
  118. remove_file "test"
  119. remove_file "vendor"
  120. end
  121. end
  122. 1 def assets_manifest
  123. template "rails/engine_manifest.js", "app/assets/config/#{underscored_name}_manifest.js"
  124. end
  125. 1 def stylesheets
  126. if mountable?
  127. copy_file "rails/stylesheets.css",
  128. "app/assets/stylesheets/#{namespaced_name}/application.css"
  129. elsif full?
  130. empty_directory_with_keep_file "app/assets/stylesheets/#{namespaced_name}"
  131. end
  132. end
  133. 1 def bin(force = false)
  134. bin_file = engine? ? "bin/rails.tt" : "bin/test.tt"
  135. template bin_file, force: force do |content|
  136. "#{shebang}\n" + content
  137. end
  138. chmod "bin", 0755, verbose: false
  139. end
  140. 1 def gemfile_entry
  141. return unless inside_application?
  142. gemfile_in_app_path = File.join(rails_app_path, "Gemfile")
  143. if File.exist? gemfile_in_app_path
  144. entry = "\ngem '#{name}', path: '#{relative_path}'"
  145. append_file gemfile_in_app_path, entry
  146. end
  147. end
  148. end
  149. 1 module Generators
  150. 1 class PluginGenerator < AppBase # :nodoc:
  151. 1 add_shared_options_for "plugin"
  152. 1 alias_method :plugin_path, :app_path
  153. 1 class_option :dummy_path, type: :string, default: "test/dummy",
  154. desc: "Create dummy application at given path"
  155. 1 class_option :full, type: :boolean, default: false,
  156. desc: "Generate a rails engine with bundled Rails application for testing"
  157. 1 class_option :mountable, type: :boolean, default: false,
  158. desc: "Generate mountable isolated engine"
  159. 1 class_option :skip_gemspec, type: :boolean, default: false,
  160. desc: "Skip gemspec file"
  161. 1 class_option :skip_gemfile_entry, type: :boolean, default: false,
  162. desc: "If creating plugin in application's directory " \
  163. "skip adding entry to Gemfile"
  164. 1 class_option :api, type: :boolean, default: false,
  165. desc: "Generate a smaller stack for API application plugins"
  166. 1 def initialize(*args)
  167. @dummy_path = nil
  168. super
  169. end
  170. 1 public_task :set_default_accessors!
  171. 1 public_task :create_root
  172. 1 def create_root_files
  173. build(:readme)
  174. build(:rakefile)
  175. build(:gemspec) unless options[:skip_gemspec]
  176. build(:license)
  177. build(:gitignore) unless options[:skip_git]
  178. build(:gemfile) unless options[:skip_gemfile]
  179. build(:version_control)
  180. end
  181. 1 def create_app_files
  182. build(:app)
  183. end
  184. 1 def create_config_files
  185. build(:config)
  186. end
  187. 1 def create_lib_files
  188. build(:lib)
  189. end
  190. 1 def create_assets_manifest_file
  191. build(:assets_manifest) if !api? && engine?
  192. end
  193. 1 def create_public_stylesheets_files
  194. build(:stylesheets) unless api?
  195. end
  196. 1 def create_bin_files
  197. build(:bin)
  198. end
  199. 1 def create_test_files
  200. build(:test) unless options[:skip_test]
  201. end
  202. 1 def create_test_dummy_files
  203. return unless with_dummy_app?
  204. create_dummy_app
  205. end
  206. 1 def update_gemfile
  207. build(:gemfile_entry) unless options[:skip_gemfile_entry]
  208. end
  209. 1 def finish_template
  210. build(:leftovers)
  211. end
  212. 1 public_task :apply_rails_template
  213. 1 def name
  214. @name ||= begin
  215. # same as ActiveSupport::Inflector#underscore except not replacing '-'
  216. underscored = original_name.dup
  217. underscored.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
  218. underscored.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
  219. underscored.downcase!
  220. underscored
  221. end
  222. end
  223. 1 def underscored_name
  224. @underscored_name ||= original_name.underscore
  225. end
  226. 1 def namespaced_name
  227. @namespaced_name ||= name.tr("-", "/")
  228. end
  229. 1 private
  230. 1 def create_dummy_app(path = nil)
  231. dummy_path(path) if path
  232. say_status :vendor_app, dummy_path
  233. mute do
  234. build(:generate_test_dummy)
  235. build(:test_dummy_config)
  236. build(:test_dummy_assets)
  237. build(:test_dummy_clean)
  238. # ensure that bin/rails has proper dummy_path
  239. build(:bin, true)
  240. end
  241. end
  242. 1 def engine?
  243. full? || mountable? || options[:engine]
  244. end
  245. 1 def full?
  246. options[:full]
  247. end
  248. 1 def mountable?
  249. options[:mountable]
  250. end
  251. 1 def skip_git?
  252. options[:skip_git]
  253. end
  254. 1 def with_dummy_app?
  255. options[:skip_test].blank? || options[:dummy_path] != "test/dummy"
  256. end
  257. 1 def api?
  258. options[:api]
  259. end
  260. 1 def self.banner
  261. "rails plugin new #{arguments.map(&:usage).join(' ')} [options]"
  262. end
  263. 1 def original_name
  264. @original_name ||= File.basename(destination_root)
  265. end
  266. 1 def modules
  267. @modules ||= namespaced_name.camelize.split("::")
  268. end
  269. 1 def wrap_in_modules(unwrapped_code)
  270. unwrapped_code = "#{unwrapped_code}".strip.gsub(/\s$\n/, "")
  271. modules.reverse.inject(unwrapped_code) do |content, mod|
  272. str = +"module #{mod}\n"
  273. str << content.lines.map { |line| " #{line}" }.join
  274. str << (content.present? ? "\nend" : "end")
  275. end
  276. end
  277. 1 def camelized_modules
  278. @camelized_modules ||= namespaced_name.camelize
  279. end
  280. 1 def humanized
  281. @humanized ||= original_name.underscore.humanize
  282. end
  283. 1 def camelized
  284. @camelized ||= name.gsub(/\W/, "_").squeeze("_").camelize
  285. end
  286. 1 def author
  287. default = "TODO: Write your name"
  288. if skip_git?
  289. @author = default
  290. else
  291. @author = `git config user.name`.chomp rescue default
  292. end
  293. end
  294. 1 def email
  295. default = "TODO: Write your email address"
  296. if skip_git?
  297. @email = default
  298. else
  299. @email = `git config user.email`.chomp rescue default
  300. end
  301. end
  302. 1 def valid_const?
  303. if /-\d/.match?(original_name)
  304. raise Error, "Invalid plugin name #{original_name}. Please give a name which does not contain a namespace starting with numeric characters."
  305. elsif /[^\w-]+/.match?(original_name)
  306. raise Error, "Invalid plugin name #{original_name}. Please give a name which uses only alphabetic, numeric, \"_\" or \"-\" characters."
  307. elsif /^\d/.match?(camelized)
  308. raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers."
  309. elsif RESERVED_NAMES.include?(name)
  310. raise Error, "Invalid plugin name #{original_name}. Please give a " \
  311. "name which does not match one of the reserved rails " \
  312. "words: #{RESERVED_NAMES.join(", ")}"
  313. elsif Object.const_defined?(camelized)
  314. raise Error, "Invalid plugin name #{original_name}, constant #{camelized} is already in use. Please choose another plugin name."
  315. end
  316. end
  317. 1 def get_builder_class
  318. defined?(::PluginBuilder) ? ::PluginBuilder : Rails::PluginBuilder
  319. end
  320. 1 def rakefile_test_tasks
  321. <<-RUBY
  322. require "rake/testtask"
  323. Rake::TestTask.new(:test) do |t|
  324. t.libs << 'test'
  325. t.pattern = 'test/**/*_test.rb'
  326. t.verbose = false
  327. end
  328. RUBY
  329. end
  330. 1 def dummy_path(path = nil)
  331. @dummy_path = path if path
  332. @dummy_path || options[:dummy_path]
  333. end
  334. 1 def mute(&block)
  335. shell.mute(&block)
  336. end
  337. 1 def rails_app_path
  338. APP_PATH.sub("/config/application", "") if defined?(APP_PATH)
  339. end
  340. 1 def inside_application?
  341. rails_app_path && destination_root.start_with?(rails_app_path.to_s)
  342. end
  343. 1 def relative_path
  344. return unless inside_application?
  345. app_path.delete_prefix("#{rails_app_path}/")
  346. end
  347. end
  348. end
  349. end

lib/rails/generators/rails/resource/resource_generator.rb

90.0% lines covered

10 relevant lines. 9 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rails/generators/resource_helpers"
  3. 1 require "rails/generators/rails/model/model_generator"
  4. 1 module Rails
  5. 1 module Generators
  6. 1 class ResourceGenerator < ModelGenerator # :nodoc:
  7. 1 include ResourceHelpers
  8. 1 hook_for :resource_controller, required: true do |controller|
  9. invoke controller, [ controller_name, options[:actions] ]
  10. end
  11. 1 class_option :actions, type: :array, banner: "ACTION ACTION", default: [],
  12. desc: "Actions for the resource controller"
  13. 1 hook_for :resource_route, required: true
  14. end
  15. end
  16. end

lib/rails/generators/rails/resource_route/resource_route_generator.rb

0.0% lines covered

10 relevant lines. 0 lines covered and 10 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Generators
  4. class ResourceRouteGenerator < NamedBase # :nodoc:
  5. # Properly nests namespaces passed into a generator
  6. #
  7. # $ bin/rails generate resource admin/users/products
  8. #
  9. # should give you
  10. #
  11. # namespace :admin do
  12. # namespace :users do
  13. # resources :products
  14. # end
  15. # end
  16. def add_resource_route
  17. return if options[:actions].present?
  18. route "resources :#{file_name.pluralize}", namespace: regular_class_path
  19. end
  20. end
  21. end
  22. end

lib/rails/generators/rails/scaffold/scaffold_generator.rb

76.19% lines covered

21 relevant lines. 16 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rails/generators/rails/resource/resource_generator"
  3. 1 module Rails
  4. 1 module Generators
  5. 1 class ScaffoldGenerator < ResourceGenerator # :nodoc:
  6. 1 remove_hook_for :resource_controller
  7. 1 remove_class_option :actions
  8. 1 class_option :api, type: :boolean
  9. 1 class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
  10. 1 class_option :stylesheet_engine, desc: "Engine for Stylesheets"
  11. 1 class_option :assets, type: :boolean
  12. 1 class_option :resource_route, type: :boolean
  13. 1 class_option :scaffold_stylesheet, type: :boolean
  14. 1 def handle_skip
  15. @options = @options.merge(stylesheets: false) unless options[:assets]
  16. @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets] && options[:scaffold_stylesheet]
  17. end
  18. 1 hook_for :scaffold_controller, required: true
  19. 1 hook_for :assets do |assets|
  20. invoke assets, [controller_name]
  21. end
  22. 1 hook_for :stylesheet_engine do |stylesheet_engine|
  23. if behavior == :invoke
  24. invoke stylesheet_engine, [controller_name]
  25. end
  26. end
  27. end
  28. end
  29. end

lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb

63.33% lines covered

30 relevant lines. 19 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "rails/generators/resource_helpers"
  3. 1 module Rails
  4. 1 module Generators
  5. 1 class ScaffoldControllerGenerator < NamedBase # :nodoc:
  6. 1 include ResourceHelpers
  7. 1 check_class_collision suffix: "Controller"
  8. 1 class_option :helper, type: :boolean
  9. 1 class_option :orm, banner: "NAME", type: :string, required: true,
  10. desc: "ORM to generate the controller for"
  11. 1 class_option :api, type: :boolean,
  12. desc: "Generates API controller"
  13. 1 class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
  14. 1 argument :attributes, type: :array, default: [], banner: "field:type field:type"
  15. 1 def create_controller_files
  16. template_file = options.api? ? "api_controller.rb" : "controller.rb"
  17. template template_file, File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb")
  18. end
  19. 1 hook_for :template_engine, as: :scaffold do |template_engine|
  20. invoke template_engine unless options.api?
  21. end
  22. 1 hook_for :resource_route, required: true do |route|
  23. invoke route unless options.skip_routes?
  24. end
  25. 1 hook_for :test_framework, as: :scaffold
  26. # Invoke the helper using the controller name (pluralized)
  27. 1 hook_for :helper, as: :scaffold do |invoked|
  28. invoke invoked, [ controller_name ]
  29. end
  30. 1 private
  31. 1 def permitted_params
  32. attachments, others = attributes_names.partition { |name| attachments?(name) }
  33. params = others.map { |name| ":#{name}" }
  34. params += attachments.map { |name| "#{name}: []" }
  35. params.join(", ")
  36. end
  37. 1 def attachments?(name)
  38. attribute = attributes.find { |attr| attr.name == name }
  39. attribute&.attachments?
  40. end
  41. end
  42. end
  43. end

lib/rails/generators/rails/system_test/system_test_generator.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rails
  3. 1 module Generators
  4. 1 class SystemTestGenerator < NamedBase # :nodoc:
  5. 1 hook_for :system_tests, as: :system
  6. end
  7. end
  8. end

lib/rails/generators/rails/task/task_generator.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 module Rails
  3. 1 module Generators
  4. 1 class TaskGenerator < NamedBase # :nodoc:
  5. 1 argument :actions, type: :array, default: [], banner: "action action"
  6. 1 def create_task_files
  7. template "task.rb", File.join("lib/tasks", "#{file_name}.rake")
  8. end
  9. end
  10. end
  11. end

lib/rails/generators/resource_helpers.rb

43.9% lines covered

41 relevant lines. 18 lines covered and 23 lines missed.
    
  1. # frozen_string_literal: true
  2. 2 require "rails/generators/active_model"
  3. 2 require "rails/generators/model_helpers"
  4. 2 module Rails
  5. 2 module Generators
  6. # Deal with controller names on scaffold and add some helpers to deal with
  7. # ActiveModel.
  8. 2 module ResourceHelpers # :nodoc:
  9. 2 def self.included(base) #:nodoc:
  10. 2 base.include(Rails::Generators::ModelHelpers)
  11. 2 base.class_option :model_name, type: :string, desc: "ModelName to be used"
  12. end
  13. # Set controller variables on initialization.
  14. 2 def initialize(*args) #:nodoc:
  15. super
  16. controller_name = name
  17. if options[:model_name]
  18. self.name = options[:model_name]
  19. assign_names!(name)
  20. end
  21. assign_controller_names!(controller_name.pluralize)
  22. end
  23. 2 private
  24. 2 attr_reader :controller_name, :controller_file_name
  25. 2 def controller_class_path
  26. if options[:model_name]
  27. @controller_class_path
  28. else
  29. class_path
  30. end
  31. end
  32. 2 def assign_controller_names!(name)
  33. @controller_name = name
  34. @controller_class_path = name.include?("/") ? name.split("/") : name.split("::")
  35. @controller_class_path.map!(&:underscore)
  36. @controller_file_name = @controller_class_path.pop
  37. end
  38. 2 def controller_file_path
  39. @controller_file_path ||= (controller_class_path + [controller_file_name]).join("/")
  40. end
  41. 2 def controller_class_name
  42. (controller_class_path + [controller_file_name]).map!(&:camelize).join("::")
  43. end
  44. 2 def controller_i18n_scope
  45. @controller_i18n_scope ||= controller_file_path.tr("/", ".")
  46. end
  47. # Loads the ORM::Generators::ActiveModel class. This class is responsible
  48. # to tell scaffold entities how to generate a specific method for the
  49. # ORM. Check Rails::Generators::ActiveModel for more information.
  50. 2 def orm_class
  51. @orm_class ||= begin
  52. # Raise an error if the class_option :orm was not defined.
  53. unless self.class.class_options[:orm]
  54. raise "You need to have :orm as class option to invoke orm_class and orm_instance"
  55. end
  56. begin
  57. "#{options[:orm].to_s.camelize}::Generators::ActiveModel".constantize
  58. rescue NameError
  59. Rails::Generators::ActiveModel
  60. end
  61. end
  62. end
  63. # Initialize ORM::Generators::ActiveModel to access instance methods.
  64. 2 def orm_instance(name = singular_table_name)
  65. @orm_instance ||= orm_class.new(name)
  66. end
  67. end
  68. end
  69. end

lib/rails/generators/test_case.rb

100.0% lines covered

13 relevant lines. 13 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 require "rails/generators"
  3. 16 require "rails/generators/testing/behaviour"
  4. 16 require "rails/generators/testing/setup_and_teardown"
  5. 16 require "rails/generators/testing/assertions"
  6. 16 require "fileutils"
  7. 16 module Rails
  8. 16 module Generators
  9. # Disable color in output. Easier to debug.
  10. 16 no_color!
  11. # This class provides a TestCase for testing generators. To set up, you need
  12. # just to configure the destination and set which generator is being tested:
  13. #
  14. # class AppGeneratorTest < Rails::Generators::TestCase
  15. # tests AppGenerator
  16. # destination File.expand_path("../tmp", __dir__)
  17. # end
  18. #
  19. # If you want to ensure your destination root is clean before running each test,
  20. # you can set a setup callback:
  21. #
  22. # class AppGeneratorTest < Rails::Generators::TestCase
  23. # tests AppGenerator
  24. # destination File.expand_path("../tmp", __dir__)
  25. # setup :prepare_destination
  26. # end
  27. 16 class TestCase < ActiveSupport::TestCase
  28. 16 include Rails::Generators::Testing::Behaviour
  29. 16 include Rails::Generators::Testing::SetupAndTeardown
  30. 16 include Rails::Generators::Testing::Assertions
  31. 16 include FileUtils
  32. end
  33. end
  34. end

lib/rails/generators/test_unit.rb

0.0% lines covered

7 relevant lines. 0 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/named_base"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class Base < Rails::Generators::NamedBase # :nodoc:
  6. end
  7. end
  8. end

lib/rails/generators/test_unit/controller/controller_generator.rb

0.0% lines covered

14 relevant lines. 0 lines covered and 14 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class ControllerGenerator < Base # :nodoc:
  6. argument :actions, type: :array, default: [], banner: "action action"
  7. class_option :skip_routes, type: :boolean
  8. check_class_collision suffix: "ControllerTest"
  9. def create_test_files
  10. template "functional_test.rb",
  11. File.join("test/controllers", class_path, "#{file_name}_controller_test.rb")
  12. end
  13. end
  14. end
  15. end

lib/rails/generators/test_unit/generator/generator_generator.rb

0.0% lines covered

21 relevant lines. 0 lines covered and 21 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class GeneratorGenerator < Base # :nodoc:
  6. check_class_collision suffix: "GeneratorTest"
  7. class_option :namespace, type: :boolean, default: true,
  8. desc: "Namespace generator under lib/generators/name"
  9. def create_generator_files
  10. template "generator_test.rb", File.join("test/lib/generators", class_path, "#{file_name}_generator_test.rb")
  11. end
  12. private
  13. def generator_path
  14. if options[:namespace]
  15. File.join("generators", regular_class_path, file_name, "#{file_name}_generator")
  16. else
  17. File.join("generators", regular_class_path, "#{file_name}_generator")
  18. end
  19. end
  20. end
  21. end
  22. end

lib/rails/generators/test_unit/helper/helper_generator.rb

0.0% lines covered

7 relevant lines. 0 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class HelperGenerator < Base # :nodoc:
  6. # Rails does not generate anything here.
  7. end
  8. end
  9. end

lib/rails/generators/test_unit/integration/integration_generator.rb

0.0% lines covered

15 relevant lines. 0 lines covered and 15 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class IntegrationGenerator < Base # :nodoc:
  6. check_class_collision suffix: "Test"
  7. def create_test_files
  8. template "integration_test.rb", File.join("test/integration", class_path, "#{file_name}_test.rb")
  9. end
  10. private
  11. def file_name
  12. @_file_name ||= super.sub(/_test\z/i, "")
  13. end
  14. end
  15. end
  16. end

lib/rails/generators/test_unit/job/job_generator.rb

0.0% lines covered

15 relevant lines. 0 lines covered and 15 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class JobGenerator < Base # :nodoc:
  6. check_class_collision suffix: "JobTest"
  7. def create_test_file
  8. template "unit_test.rb", File.join("test/jobs", class_path, "#{file_name}_job_test.rb")
  9. end
  10. private
  11. def file_name
  12. @_file_name ||= super.sub(/_job\z/i, "")
  13. end
  14. end
  15. end
  16. end

lib/rails/generators/test_unit/mailer/mailer_generator.rb

0.0% lines covered

21 relevant lines. 0 lines covered and 21 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class MailerGenerator < Base # :nodoc:
  6. argument :actions, type: :array, default: [], banner: "method method"
  7. def check_class_collision
  8. class_collisions "#{class_name}MailerTest", "#{class_name}MailerPreview"
  9. end
  10. def create_test_files
  11. template "functional_test.rb", File.join("test/mailers", class_path, "#{file_name}_mailer_test.rb")
  12. end
  13. def create_preview_files
  14. template "preview.rb", File.join("test/mailers/previews", class_path, "#{file_name}_mailer_preview.rb")
  15. end
  16. private
  17. def file_name
  18. @_file_name ||= super.sub(/_mailer\z/i, "")
  19. end
  20. end
  21. end
  22. end

lib/rails/generators/test_unit/model/model_generator.rb

0.0% lines covered

28 relevant lines. 0 lines covered and 28 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class ModelGenerator < Base # :nodoc:
  6. RESERVED_YAML_KEYWORDS = %w(y yes n no true false on off null)
  7. argument :attributes, type: :array, default: [], banner: "field:type field:type"
  8. class_option :fixture, type: :boolean
  9. check_class_collision suffix: "Test"
  10. def create_test_file
  11. template "unit_test.rb", File.join("test/models", class_path, "#{file_name}_test.rb")
  12. end
  13. hook_for :fixture_replacement
  14. def create_fixture_file
  15. if options[:fixture] && options[:fixture_replacement].nil?
  16. template "fixtures.yml", File.join("test/fixtures", class_path, "#{fixture_file_name}.yml")
  17. end
  18. end
  19. private
  20. def yaml_key_value(key, value)
  21. if RESERVED_YAML_KEYWORDS.include?(key.downcase)
  22. "'#{key}': #{value}"
  23. else
  24. "#{key}: #{value}"
  25. end
  26. end
  27. end
  28. end
  29. end

lib/rails/generators/test_unit/plugin/plugin_generator.rb

0.0% lines covered

11 relevant lines. 0 lines covered and 11 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class PluginGenerator < Base # :nodoc:
  6. check_class_collision suffix: "Test"
  7. def create_test_files
  8. directory ".", "test"
  9. end
  10. end
  11. end
  12. end

lib/rails/generators/test_unit/plugin/templates/test_helper.rb

0.0% lines covered

2 relevant lines. 0 lines covered and 2 lines missed.
    
  1. require "active_support/testing/autorun"
  2. require "active_support"

lib/rails/generators/test_unit/scaffold/scaffold_generator.rb

0.0% lines covered

53 relevant lines. 0 lines covered and 53 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. require "rails/generators/resource_helpers"
  4. module TestUnit # :nodoc:
  5. module Generators # :nodoc:
  6. class ScaffoldGenerator < Base # :nodoc:
  7. include Rails::Generators::ResourceHelpers
  8. check_class_collision suffix: "ControllerTest"
  9. class_option :api, type: :boolean,
  10. desc: "Generates API functional tests"
  11. class_option :system_tests, type: :string,
  12. desc: "Skip system test files"
  13. argument :attributes, type: :array, default: [], banner: "field:type field:type"
  14. def create_test_files
  15. template_file = options.api? ? "api_functional_test.rb" : "functional_test.rb"
  16. template template_file,
  17. File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb")
  18. if !options.api? && options[:system_tests]
  19. template "system_test.rb", File.join("test/system", class_path, "#{file_name.pluralize}_test.rb")
  20. end
  21. end
  22. def fixture_name
  23. @fixture_name ||=
  24. if mountable_engine?
  25. (namespace_dirs + [table_name]).join("_")
  26. else
  27. table_name
  28. end
  29. end
  30. private
  31. def attributes_string
  32. attributes_hash.map { |k, v| "#{k}: #{v}" }.join(", ")
  33. end
  34. def attributes_hash
  35. return {} if attributes_names.empty?
  36. attributes_names.map do |name|
  37. if %w(password password_confirmation).include?(name) && attributes.any?(&:password_digest?)
  38. ["#{name}", "'secret'"]
  39. elsif !virtual?(name)
  40. ["#{name}", "@#{singular_table_name}.#{name}"]
  41. end
  42. end.compact.sort.to_h
  43. end
  44. def boolean?(name)
  45. attribute = attributes.find { |attr| attr.name == name }
  46. attribute&.type == :boolean
  47. end
  48. def virtual?(name)
  49. attribute = attributes.find { |attr| attr.name == name }
  50. attribute&.virtual?
  51. end
  52. end
  53. end
  54. end

lib/rails/generators/test_unit/system/system_generator.rb

0.0% lines covered

18 relevant lines. 0 lines covered and 18 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/test_unit"
  3. module TestUnit # :nodoc:
  4. module Generators # :nodoc:
  5. class SystemGenerator < Base # :nodoc:
  6. check_class_collision suffix: "Test"
  7. def create_test_files
  8. if !File.exist?(File.join("test/application_system_test_case.rb"))
  9. template "application_system_test_case.rb", File.join("test", "application_system_test_case.rb")
  10. end
  11. template "system_test.rb", File.join("test/system", class_path, "#{file_name.pluralize}_test.rb")
  12. end
  13. private
  14. def file_name
  15. @_file_name ||= super.sub(/_test\z/i, "")
  16. end
  17. end
  18. end
  19. end

lib/rails/generators/testing/assertions.rb

40.54% lines covered

37 relevant lines. 15 lines covered and 22 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 module Rails
  3. 16 module Generators
  4. 16 module Testing
  5. 16 module Assertions
  6. # Asserts a given file exists. You need to supply an absolute path or a path relative
  7. # to the configured destination:
  8. #
  9. # assert_file "config/environment.rb"
  10. #
  11. # You can also give extra arguments. If the argument is a regexp, it will check if the
  12. # regular expression matches the given file content. If it's a string, it compares the
  13. # file with the given string:
  14. #
  15. # assert_file "config/environment.rb", /initialize/
  16. #
  17. # Finally, when a block is given, it yields the file content:
  18. #
  19. # assert_file "app/controllers/products_controller.rb" do |controller|
  20. # assert_instance_method :index, controller do |index|
  21. # assert_match(/Product\.all/, index)
  22. # end
  23. # end
  24. 16 def assert_file(relative, *contents)
  25. absolute = File.expand_path(relative, destination_root)
  26. assert File.exist?(absolute), "Expected file #{relative.inspect} to exist, but does not"
  27. read = File.read(absolute) if block_given? || !contents.empty?
  28. assert_nothing_raised { yield read } if block_given?
  29. contents.each do |content|
  30. case content
  31. when String
  32. assert_equal content, read
  33. when Regexp
  34. assert_match content, read
  35. end
  36. end
  37. end
  38. 16 alias :assert_directory :assert_file
  39. # Asserts a given file does not exist. You need to supply an absolute path or a
  40. # path relative to the configured destination:
  41. #
  42. # assert_no_file "config/random.rb"
  43. 16 def assert_no_file(relative)
  44. absolute = File.expand_path(relative, destination_root)
  45. assert !File.exist?(absolute), "Expected file #{relative.inspect} to not exist, but does"
  46. end
  47. 16 alias :assert_no_directory :assert_no_file
  48. # Asserts a given migration exists. You need to supply an absolute path or a
  49. # path relative to the configured destination:
  50. #
  51. # assert_migration "db/migrate/create_products.rb"
  52. #
  53. # This method manipulates the given path and tries to find any migration which
  54. # matches the migration name. For example, the call above is converted to:
  55. #
  56. # assert_file "db/migrate/003_create_products.rb"
  57. #
  58. # Consequently, assert_migration accepts the same arguments has assert_file.
  59. 16 def assert_migration(relative, *contents, &block)
  60. file_name = migration_file_name(relative)
  61. assert file_name, "Expected migration #{relative} to exist, but was not found"
  62. assert_file file_name, *contents, &block
  63. end
  64. # Asserts a given migration does not exist. You need to supply an absolute path or a
  65. # path relative to the configured destination:
  66. #
  67. # assert_no_migration "db/migrate/create_products.rb"
  68. 16 def assert_no_migration(relative)
  69. file_name = migration_file_name(relative)
  70. assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}"
  71. end
  72. # Asserts the given class method exists in the given content. This method does not detect
  73. # class methods inside (class << self), only class methods which starts with "self.".
  74. # When a block is given, it yields the content of the method.
  75. #
  76. # assert_migration "db/migrate/create_products.rb" do |migration|
  77. # assert_class_method :up, migration do |up|
  78. # assert_match(/create_table/, up)
  79. # end
  80. # end
  81. 16 def assert_class_method(method, content, &block)
  82. assert_instance_method "self.#{method}", content, &block
  83. end
  84. # Asserts the given method exists in the given content. When a block is given,
  85. # it yields the content of the method.
  86. #
  87. # assert_file "app/controllers/products_controller.rb" do |controller|
  88. # assert_instance_method :index, controller do |index|
  89. # assert_match(/Product\.all/, index)
  90. # end
  91. # end
  92. 16 def assert_instance_method(method, content)
  93. assert content =~ /(\s+)def #{method}(\(.+\))?(.*?)\n\1end/m, "Expected to have method #{method}"
  94. assert_nothing_raised { yield $3.strip } if block_given?
  95. end
  96. 16 alias :assert_method :assert_instance_method
  97. # Asserts the given attribute type gets translated to a field type
  98. # properly:
  99. #
  100. # assert_field_type :date, :date_select
  101. 16 def assert_field_type(attribute_type, field_type)
  102. assert_equal(field_type, create_generated_attribute(attribute_type).field_type)
  103. end
  104. # Asserts the given attribute type gets a proper default value:
  105. #
  106. # assert_field_default_value :string, "MyString"
  107. 16 def assert_field_default_value(attribute_type, value)
  108. if value.nil?
  109. assert_nil(create_generated_attribute(attribute_type).default)
  110. else
  111. assert_equal(value, create_generated_attribute(attribute_type).default)
  112. end
  113. end
  114. end
  115. end
  116. end
  117. end

lib/rails/generators/testing/behaviour.rb

70.21% lines covered

47 relevant lines. 33 lines covered and 14 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 require "active_support/core_ext/class/attribute"
  3. 16 require "active_support/core_ext/module/delegation"
  4. 16 require "active_support/core_ext/hash/reverse_merge"
  5. 16 require "active_support/core_ext/kernel/reporting"
  6. 16 require "active_support/testing/stream"
  7. 16 require "active_support/concern"
  8. 16 require "rails/generators"
  9. 16 module Rails
  10. 16 module Generators
  11. 16 module Testing
  12. 16 module Behaviour
  13. 16 extend ActiveSupport::Concern
  14. 16 include ActiveSupport::Testing::Stream
  15. 16 included do
  16. # Generators frequently change the current path using +FileUtils.cd+.
  17. # So we need to store the path at file load and revert back to it after each test.
  18. 16 class_attribute :current_path, default: File.expand_path(Dir.pwd)
  19. 16 class_attribute :default_arguments, default: []
  20. 16 class_attribute :destination_root
  21. 16 class_attribute :generator_class
  22. end
  23. 16 module ClassMethods
  24. # Sets which generator should be tested:
  25. #
  26. # tests AppGenerator
  27. 16 def tests(klass)
  28. 22 self.generator_class = klass
  29. end
  30. # Sets default arguments on generator invocation. This can be overwritten when
  31. # invoking it.
  32. #
  33. # arguments %w(app_name --skip-active-record)
  34. 16 def arguments(array)
  35. 14 self.default_arguments = array
  36. end
  37. # Sets the destination of generator files:
  38. #
  39. # destination File.expand_path("../tmp", __dir__)
  40. 16 def destination(path)
  41. 19 self.destination_root = path
  42. end
  43. end
  44. # Runs the generator configured for this class. The first argument is an array like
  45. # command line arguments:
  46. #
  47. # class AppGeneratorTest < Rails::Generators::TestCase
  48. # tests AppGenerator
  49. # destination File.expand_path("../tmp", __dir__)
  50. # setup :prepare_destination
  51. #
  52. # test "database.yml is not created when skipping Active Record" do
  53. # run_generator %w(myapp --skip-active-record)
  54. # assert_no_file "config/database.yml"
  55. # end
  56. # end
  57. #
  58. # You can provide a configuration hash as second argument. This method returns the output
  59. # printed by the generator.
  60. 16 def run_generator(args = default_arguments, config = {})
  61. capture(:stdout) do
  62. args += ["--skip-bundle"] unless args.include? "--dev"
  63. args |= ["--skip-bootsnap"] unless args.include? "--no-skip-bootsnap"
  64. args |= ["--skip-webpack-install"] unless args.include? "--no-skip-webpack-install"
  65. generator_class.start(args, config.reverse_merge(destination_root: destination_root))
  66. end
  67. end
  68. # Instantiate the generator.
  69. 16 def generator(args = default_arguments, options = {}, config = {})
  70. @generator ||= generator_class.new(args, options, config.reverse_merge(destination_root: destination_root))
  71. end
  72. # Create a Rails::Generators::GeneratedAttribute by supplying the
  73. # attribute type and, optionally, the attribute name:
  74. #
  75. # create_generated_attribute(:string, 'name')
  76. 16 def create_generated_attribute(attribute_type, name = "test", index = nil)
  77. Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(":"))
  78. end
  79. 16 private
  80. 16 def destination_root_is_set?
  81. raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
  82. end
  83. 16 def ensure_current_path
  84. cd current_path
  85. end
  86. # Clears all files and directories in destination.
  87. 16 def prepare_destination # :doc:
  88. rm_rf(destination_root)
  89. mkdir_p(destination_root)
  90. end
  91. 16 def migration_file_name(relative)
  92. absolute = File.expand_path(relative, destination_root)
  93. dirname, file_name = File.dirname(absolute), File.basename(absolute).delete_suffix(".rb")
  94. Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
  95. end
  96. end
  97. end
  98. end
  99. end

lib/rails/generators/testing/setup_and_teardown.rb

54.55% lines covered

11 relevant lines. 6 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. 16 module Rails
  3. 16 module Generators
  4. 16 module Testing
  5. 16 module SetupAndTeardown
  6. 16 def setup # :nodoc:
  7. destination_root_is_set?
  8. ensure_current_path
  9. super
  10. end
  11. 16 def teardown # :nodoc:
  12. ensure_current_path
  13. super
  14. end
  15. end
  16. end
  17. end
  18. end

lib/rails/info.rb

0.0% lines covered

77 relevant lines. 0 lines covered and 77 lines missed.
    
  1. # frozen_string_literal: true
  2. require "cgi"
  3. module Rails
  4. # This module helps build the runtime properties that are displayed in
  5. # Rails::InfoController responses. These include the active Rails version,
  6. # Ruby version, Rack version, and so on.
  7. module Info
  8. mattr_accessor :properties, default: []
  9. class << @@properties
  10. def names
  11. map(&:first)
  12. end
  13. def value_for(property_name)
  14. if property = assoc(property_name)
  15. property.last
  16. end
  17. end
  18. end
  19. class << self #:nodoc:
  20. def property(name, value = nil)
  21. value ||= yield
  22. properties << [name, value] if value
  23. rescue Exception
  24. end
  25. def to_s
  26. column_width = properties.names.map(&:length).max
  27. info = properties.map do |name, value|
  28. value = value.join(", ") if value.is_a?(Array)
  29. "%-#{column_width}s %s" % [name, value]
  30. end
  31. info.unshift "About your application's environment"
  32. info * "\n"
  33. end
  34. alias inspect to_s
  35. def to_html
  36. (+"<table>").tap do |table|
  37. properties.each do |(name, value)|
  38. table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
  39. formatted_value = if value.kind_of?(Array)
  40. "<ul>" + value.map { |v| "<li>#{CGI.escapeHTML(v.to_s)}</li>" }.join + "</ul>"
  41. else
  42. CGI.escapeHTML(value.to_s)
  43. end
  44. table << %(<td class="value">#{formatted_value}</td></tr>)
  45. end
  46. table << "</table>"
  47. end
  48. end
  49. end
  50. # The Rails version.
  51. property "Rails version" do
  52. Rails.version.to_s
  53. end
  54. # The Ruby version and platform, e.g. "2.0.0-p247 (x86_64-darwin12.4.0)".
  55. property "Ruby version" do
  56. RUBY_DESCRIPTION
  57. end
  58. # The RubyGems version, if it's installed.
  59. property "RubyGems version" do
  60. Gem::VERSION
  61. end
  62. property "Rack version" do
  63. ::Rack.release
  64. end
  65. property "JavaScript Runtime" do
  66. ExecJS.runtime.name
  67. end
  68. property "Middleware" do
  69. Rails.configuration.middleware.map(&:inspect)
  70. end
  71. # The application's location on the filesystem.
  72. property "Application root" do
  73. File.expand_path(Rails.root)
  74. end
  75. # The current Rails environment (development, test, or production).
  76. property "Environment" do
  77. Rails.env
  78. end
  79. # The name of the database adapter for the current environment.
  80. property "Database adapter" do
  81. ActiveRecord::Base.connection.pool.db_config.adapter
  82. end
  83. property "Database schema version" do
  84. ActiveRecord::Base.connection.migration_context.current_version rescue nil
  85. end
  86. end
  87. end

lib/rails/info_controller.rb

0.0% lines covered

36 relevant lines. 0 lines covered and 36 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/application_controller"
  3. require "action_dispatch/routing/inspector"
  4. class Rails::InfoController < Rails::ApplicationController # :nodoc:
  5. prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
  6. layout -> { request.xhr? ? false : "application" }
  7. before_action :require_local!
  8. def index
  9. redirect_to action: :routes
  10. end
  11. def properties
  12. @info = Rails::Info.to_html
  13. @page_title = "Properties"
  14. end
  15. def routes
  16. if path = params[:path]
  17. path = URI::DEFAULT_PARSER.escape path
  18. normalized_path = with_leading_slash path
  19. render json: {
  20. exact: match_route { |it| it.match normalized_path },
  21. fuzzy: match_route { |it| it.spec.to_s.match path }
  22. }
  23. else
  24. @routes_inspector = ActionDispatch::Routing::RoutesInspector.new(_routes.routes)
  25. @page_title = "Routes"
  26. end
  27. end
  28. private
  29. def match_route
  30. _routes.routes.select { |route|
  31. yield route.path
  32. }.map { |route| route.path.spec.to_s }
  33. end
  34. def with_leading_slash(path)
  35. ("/" + path).squeeze("/")
  36. end
  37. end

lib/rails/initializable.rb

0.0% lines covered

75 relevant lines. 0 lines covered and 75 lines missed.
    
  1. # frozen_string_literal: true
  2. require "tsort"
  3. module Rails
  4. module Initializable
  5. def self.included(base) #:nodoc:
  6. base.extend ClassMethods
  7. end
  8. class Initializer
  9. attr_reader :name, :block
  10. def initialize(name, context, options, &block)
  11. options[:group] ||= :default
  12. @name, @context, @options, @block = name, context, options, block
  13. end
  14. def before
  15. @options[:before]
  16. end
  17. def after
  18. @options[:after]
  19. end
  20. def belongs_to?(group)
  21. @options[:group] == group || @options[:group] == :all
  22. end
  23. def run(*args)
  24. @context.instance_exec(*args, &block)
  25. end
  26. def bind(context)
  27. return self if @context
  28. Initializer.new(@name, context, @options, &block)
  29. end
  30. def context_class
  31. @context.class
  32. end
  33. end
  34. class Collection < Array
  35. include TSort
  36. alias :tsort_each_node :each
  37. def tsort_each_child(initializer, &block)
  38. select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block)
  39. end
  40. def +(other)
  41. Collection.new(to_a + other.to_a)
  42. end
  43. end
  44. def run_initializers(group = :default, *args)
  45. return if instance_variable_defined?(:@ran)
  46. initializers.tsort_each do |initializer|
  47. initializer.run(*args) if initializer.belongs_to?(group)
  48. end
  49. @ran = true
  50. end
  51. def initializers
  52. @initializers ||= self.class.initializers_for(self)
  53. end
  54. module ClassMethods
  55. def initializers
  56. @initializers ||= Collection.new
  57. end
  58. def initializers_chain
  59. initializers = Collection.new
  60. ancestors.reverse_each do |klass|
  61. next unless klass.respond_to?(:initializers)
  62. initializers = initializers + klass.initializers
  63. end
  64. initializers
  65. end
  66. def initializers_for(binding)
  67. Collection.new(initializers_chain.map { |i| i.bind(binding) })
  68. end
  69. def initializer(name, opts = {}, &blk)
  70. raise ArgumentError, "A block must be passed when defining an initializer" unless blk
  71. opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
  72. initializers << Initializer.new(name, nil, opts, &blk)
  73. end
  74. end
  75. end
  76. end

lib/rails/mailers_controller.rb

0.0% lines covered

81 relevant lines. 0 lines covered and 81 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/application_controller"
  3. class Rails::MailersController < Rails::ApplicationController # :nodoc:
  4. prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
  5. around_action :set_locale, only: :preview
  6. before_action :find_preview, only: :preview
  7. before_action :require_local!, unless: :show_previews?
  8. helper_method :part_query, :locale_query
  9. content_security_policy(false)
  10. def index
  11. @previews = ActionMailer::Preview.all
  12. @page_title = "Mailer Previews"
  13. end
  14. def preview
  15. if params[:path] == @preview.preview_name
  16. @page_title = "Mailer Previews for #{@preview.preview_name}"
  17. render action: "mailer"
  18. else
  19. @email_action = File.basename(params[:path])
  20. if @preview.email_exists?(@email_action)
  21. @page_title = "Mailer Preview for #{@preview.preview_name}##{@email_action}"
  22. @email = @preview.call(@email_action, params)
  23. if params[:part]
  24. part_type = Mime::Type.lookup(params[:part])
  25. if part = find_part(part_type)
  26. response.content_type = part_type
  27. render plain: part.respond_to?(:decoded) ? part.decoded : part
  28. else
  29. raise AbstractController::ActionNotFound, "Email part '#{part_type}' not found in #{@preview.name}##{@email_action}"
  30. end
  31. else
  32. @part = find_preferred_part(request.format, Mime[:html], Mime[:text])
  33. render action: "email", layout: false, formats: [:html]
  34. end
  35. else
  36. raise AbstractController::ActionNotFound, "Email '#{@email_action}' not found in #{@preview.name}"
  37. end
  38. end
  39. end
  40. private
  41. def show_previews? # :doc:
  42. ActionMailer::Base.show_previews
  43. end
  44. def find_preview # :doc:
  45. candidates = []
  46. params[:path].to_s.scan(%r{/|$}) { candidates << $` }
  47. preview = candidates.detect { |candidate| ActionMailer::Preview.exists?(candidate) }
  48. if preview
  49. @preview = ActionMailer::Preview.find(preview)
  50. else
  51. raise AbstractController::ActionNotFound, "Mailer preview '#{params[:path]}' not found"
  52. end
  53. end
  54. def find_preferred_part(*formats) # :doc:
  55. formats.each do |format|
  56. if part = @email.find_first_mime_type(format)
  57. return part
  58. end
  59. end
  60. if formats.any? { |f| @email.mime_type == f }
  61. @email
  62. end
  63. end
  64. def find_part(format) # :doc:
  65. if part = @email.find_first_mime_type(format)
  66. part
  67. elsif @email.mime_type == format
  68. @email
  69. end
  70. end
  71. def part_query(mime_type)
  72. request.query_parameters.merge(part: mime_type).to_query
  73. end
  74. def locale_query(locale)
  75. request.query_parameters.merge(locale: locale).to_query
  76. end
  77. def set_locale
  78. I18n.with_locale(params[:locale] || I18n.default_locale) do
  79. yield
  80. end
  81. end
  82. end

lib/rails/paths.rb

0.0% lines covered

156 relevant lines. 0 lines covered and 156 lines missed.
    
  1. # frozen_string_literal: true
  2. require "pathname"
  3. module Rails
  4. module Paths
  5. # This object is an extended hash that behaves as root of the <tt>Rails::Paths</tt> system.
  6. # It allows you to collect information about how you want to structure your application
  7. # paths through a Hash-like API. It requires you to give a physical path on initialization.
  8. #
  9. # root = Root.new "/rails"
  10. # root.add "app/controllers", eager_load: true
  11. #
  12. # The above command creates a new root object and adds "app/controllers" as a path.
  13. # This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
  14. #
  15. # path = root["app/controllers"]
  16. # path.eager_load? # => true
  17. # path.is_a?(Rails::Paths::Path) # => true
  18. #
  19. # The +Path+ object is simply an enumerable and allows you to easily add extra paths:
  20. #
  21. # path.is_a?(Enumerable) # => true
  22. # path.to_ary.inspect # => ["app/controllers"]
  23. #
  24. # path << "lib/controllers"
  25. # path.to_ary.inspect # => ["app/controllers", "lib/controllers"]
  26. #
  27. # Notice that when you add a path using +add+, the path object created already
  28. # contains the path with the same path value given to +add+. In some situations,
  29. # you may not want this behavior, so you can give <tt>:with</tt> as option.
  30. #
  31. # root.add "config/routes", with: "config/routes.rb"
  32. # root["config/routes"].inspect # => ["config/routes.rb"]
  33. #
  34. # The +add+ method accepts the following options as arguments:
  35. # eager_load, autoload, autoload_once, and glob.
  36. #
  37. # Finally, the +Path+ object also provides a few helpers:
  38. #
  39. # root = Root.new "/rails"
  40. # root.add "app/controllers"
  41. #
  42. # root["app/controllers"].expanded # => ["/rails/app/controllers"]
  43. # root["app/controllers"].existent # => ["/rails/app/controllers"]
  44. #
  45. # Check the <tt>Rails::Paths::Path</tt> documentation for more information.
  46. class Root
  47. attr_accessor :path
  48. def initialize(path)
  49. @path = path
  50. @root = {}
  51. end
  52. def []=(path, value)
  53. glob = self[path] ? self[path].glob : nil
  54. add(path, with: value, glob: glob)
  55. end
  56. def add(path, options = {})
  57. with = Array(options.fetch(:with, path))
  58. @root[path] = Path.new(self, path, with, options)
  59. end
  60. def [](path)
  61. @root[path]
  62. end
  63. def values
  64. @root.values
  65. end
  66. def keys
  67. @root.keys
  68. end
  69. def values_at(*list)
  70. @root.values_at(*list)
  71. end
  72. def all_paths
  73. values.tap(&:uniq!)
  74. end
  75. def autoload_once
  76. filter_by(&:autoload_once?)
  77. end
  78. def eager_load
  79. filter_by(&:eager_load?)
  80. end
  81. def autoload_paths
  82. filter_by(&:autoload?)
  83. end
  84. def load_paths
  85. filter_by(&:load_path?)
  86. end
  87. private
  88. def filter_by(&block)
  89. all_paths.find_all(&block).flat_map { |path|
  90. paths = path.existent
  91. paths - path.children.flat_map { |p| yield(p) ? [] : p.existent }
  92. }.uniq
  93. end
  94. end
  95. class Path
  96. include Enumerable
  97. attr_accessor :glob
  98. def initialize(root, current, paths, options = {})
  99. @paths = paths
  100. @current = current
  101. @root = root
  102. @glob = options[:glob]
  103. @exclude = options[:exclude]
  104. options[:autoload_once] ? autoload_once! : skip_autoload_once!
  105. options[:eager_load] ? eager_load! : skip_eager_load!
  106. options[:autoload] ? autoload! : skip_autoload!
  107. options[:load_path] ? load_path! : skip_load_path!
  108. end
  109. def absolute_current # :nodoc:
  110. File.expand_path(@current, @root.path)
  111. end
  112. def children
  113. keys = @root.keys.find_all { |k|
  114. k.start_with?(@current) && k != @current
  115. }
  116. @root.values_at(*keys.sort)
  117. end
  118. def first
  119. expanded.first
  120. end
  121. def last
  122. expanded.last
  123. end
  124. %w(autoload_once eager_load autoload load_path).each do |m|
  125. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  126. def #{m}! # def eager_load!
  127. @#{m} = true # @eager_load = true
  128. end # end
  129. #
  130. def skip_#{m}! # def skip_eager_load!
  131. @#{m} = false # @eager_load = false
  132. end # end
  133. #
  134. def #{m}? # def eager_load?
  135. @#{m} # @eager_load
  136. end # end
  137. RUBY
  138. end
  139. def each(&block)
  140. @paths.each(&block)
  141. end
  142. def <<(path)
  143. @paths << path
  144. end
  145. alias :push :<<
  146. def concat(paths)
  147. @paths.concat paths
  148. end
  149. def unshift(*paths)
  150. @paths.unshift(*paths)
  151. end
  152. def to_ary
  153. @paths
  154. end
  155. def paths
  156. raise "You need to set a path root" unless @root.path
  157. map do |p|
  158. Pathname.new(@root.path).join(p)
  159. end
  160. end
  161. def extensions # :nodoc:
  162. $1.split(",") if @glob =~ /\{([\S]+)\}/
  163. end
  164. # Expands all paths against the root and return all unique values.
  165. def expanded
  166. raise "You need to set a path root" unless @root.path
  167. result = []
  168. each do |path|
  169. path = File.expand_path(path, @root.path)
  170. if @glob && File.directory?(path)
  171. result.concat files_in(path)
  172. else
  173. result << path
  174. end
  175. end
  176. result.uniq!
  177. result
  178. end
  179. # Returns all expanded paths but only if they exist in the filesystem.
  180. def existent
  181. expanded.select do |f|
  182. does_exist = File.exist?(f)
  183. if !does_exist && File.symlink?(f)
  184. raise "File #{f.inspect} is a symlink that does not point to a valid file"
  185. end
  186. does_exist
  187. end
  188. end
  189. def existent_directories
  190. expanded.select { |d| File.directory?(d) }
  191. end
  192. alias to_a expanded
  193. private
  194. def files_in(path)
  195. files = Dir.glob(@glob, base: path)
  196. files -= @exclude if @exclude
  197. files.map! { |file| File.join(path, file) }
  198. files.sort
  199. end
  200. end
  201. end
  202. end

lib/rails/plugin/test.rb

0.0% lines covered

5 relevant lines. 0 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/test_unit/runner"
  3. require "rails/test_unit/reporter"
  4. Rails::TestUnitReporter.executable = "bin/test"
  5. Rails::TestUnit::Runner.parse_options(ARGV)
  6. Rails::TestUnit::Runner.run(ARGV)

lib/rails/rack.rb

0.0% lines covered

5 relevant lines. 0 lines covered and 5 lines missed.
    
  1. # frozen_string_literal: true
  2. module Rails
  3. module Rack
  4. autoload :Logger, "rails/rack/logger"
  5. end
  6. end

lib/rails/rack/logger.rb

0.0% lines covered

63 relevant lines. 0 lines covered and 63 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/core_ext/time/conversions"
  3. require "active_support/core_ext/object/blank"
  4. require "active_support/log_subscriber"
  5. require "action_dispatch/http/request"
  6. require "rack/body_proxy"
  7. module Rails
  8. module Rack
  9. # Sets log tags, logs the request, calls the app, and flushes the logs.
  10. #
  11. # Log tags (+taggers+) can be an Array containing: methods that the +request+
  12. # object responds to, objects that respond to +to_s+ or Proc objects that accept
  13. # an instance of the +request+ object.
  14. class Logger < ActiveSupport::LogSubscriber
  15. def initialize(app, taggers = nil)
  16. @app = app
  17. @taggers = taggers || []
  18. end
  19. def call(env)
  20. request = ActionDispatch::Request.new(env)
  21. if logger.respond_to?(:tagged)
  22. logger.tagged(compute_tags(request)) { call_app(request, env) }
  23. else
  24. call_app(request, env)
  25. end
  26. end
  27. private
  28. def call_app(request, env) # :doc:
  29. instrumenter = ActiveSupport::Notifications.instrumenter
  30. instrumenter.start "request.action_dispatch", request: request
  31. logger.info { started_request_message(request) }
  32. status, headers, body = @app.call(env)
  33. body = ::Rack::BodyProxy.new(body) { finish(request) }
  34. [status, headers, body]
  35. rescue Exception
  36. finish(request)
  37. raise
  38. ensure
  39. ActiveSupport::LogSubscriber.flush_all!
  40. end
  41. # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
  42. def started_request_message(request) # :doc:
  43. 'Started %s "%s" for %s at %s' % [
  44. request.request_method,
  45. request.filtered_path,
  46. request.remote_ip,
  47. Time.now.to_default_s ]
  48. end
  49. def compute_tags(request) # :doc:
  50. @taggers.collect do |tag|
  51. case tag
  52. when Proc
  53. tag.call(request)
  54. when Symbol
  55. request.send(tag)
  56. else
  57. tag
  58. end
  59. end
  60. end
  61. def finish(request)
  62. instrumenter = ActiveSupport::Notifications.instrumenter
  63. instrumenter.finish "request.action_dispatch", request: request
  64. end
  65. def logger
  66. Rails.logger
  67. end
  68. end
  69. end
  70. end

lib/rails/railtie.rb

0.0% lines covered

106 relevant lines. 0 lines covered and 106 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/initializable"
  3. require "active_support/inflector"
  4. require "active_support/core_ext/module/introspection"
  5. require "active_support/core_ext/module/delegation"
  6. module Rails
  7. # <tt>Rails::Railtie</tt> is the core of the Rails framework and provides
  8. # several hooks to extend Rails and/or modify the initialization process.
  9. #
  10. # Every major component of Rails (Action Mailer, Action Controller, Active
  11. # Record, etc.) implements a railtie. Each of them is responsible for their
  12. # own initialization. This makes Rails itself absent of any component hooks,
  13. # allowing other components to be used in place of any of the Rails defaults.
  14. #
  15. # Developing a Rails extension does _not_ require implementing a railtie, but
  16. # if you need to interact with the Rails framework during or after boot, then
  17. # a railtie is needed.
  18. #
  19. # For example, an extension doing any of the following would need a railtie:
  20. #
  21. # * creating initializers
  22. # * configuring a Rails framework for the application, like setting a generator
  23. # * adding <tt>config.*</tt> keys to the environment
  24. # * setting up a subscriber with <tt>ActiveSupport::Notifications</tt>
  25. # * adding Rake tasks
  26. #
  27. # == Creating a Railtie
  28. #
  29. # To extend Rails using a railtie, create a subclass of <tt>Rails::Railtie</tt>.
  30. # This class must be loaded during the Rails boot process, and is conventionally
  31. # called <tt>MyNamespace::Railtie</tt>.
  32. #
  33. # The following example demonstrates an extension which can be used with or
  34. # without Rails.
  35. #
  36. # # lib/my_gem/railtie.rb
  37. # module MyGem
  38. # class Railtie < Rails::Railtie
  39. # end
  40. # end
  41. #
  42. # # lib/my_gem.rb
  43. # require "my_gem/railtie" if defined?(Rails::Railtie)
  44. #
  45. # == Initializers
  46. #
  47. # To add an initialization step to the Rails boot process from your railtie, just
  48. # define the initialization code with the +initializer+ macro:
  49. #
  50. # class MyRailtie < Rails::Railtie
  51. # initializer "my_railtie.configure_rails_initialization" do
  52. # # some initialization behavior
  53. # end
  54. # end
  55. #
  56. # If specified, the block can also receive the application object, in case you
  57. # need to access some application-specific configuration, like middleware:
  58. #
  59. # class MyRailtie < Rails::Railtie
  60. # initializer "my_railtie.configure_rails_initialization" do |app|
  61. # app.middleware.use MyRailtie::Middleware
  62. # end
  63. # end
  64. #
  65. # Finally, you can also pass <tt>:before</tt> and <tt>:after</tt> as options to
  66. # +initializer+, in case you want to couple it with a specific step in the
  67. # initialization process.
  68. #
  69. # == Configuration
  70. #
  71. # Railties can access a config object which contains configuration shared by all
  72. # railties and the application:
  73. #
  74. # class MyRailtie < Rails::Railtie
  75. # # Customize the ORM
  76. # config.app_generators.orm :my_railtie_orm
  77. #
  78. # # Add a to_prepare block which is executed once in production
  79. # # and before each request in development.
  80. # config.to_prepare do
  81. # MyRailtie.setup!
  82. # end
  83. # end
  84. #
  85. # == Loading Rake Tasks and Generators
  86. #
  87. # If your railtie has Rake tasks, you can tell Rails to load them through the method
  88. # +rake_tasks+:
  89. #
  90. # class MyRailtie < Rails::Railtie
  91. # rake_tasks do
  92. # load "path/to/my_railtie.tasks"
  93. # end
  94. # end
  95. #
  96. # By default, Rails loads generators from your load path. However, if you want to place
  97. # your generators at a different location, you can specify in your railtie a block which
  98. # will load them during normal generators lookup:
  99. #
  100. # class MyRailtie < Rails::Railtie
  101. # generators do
  102. # require "path/to/my_railtie_generator"
  103. # end
  104. # end
  105. #
  106. # Since filenames on the load path are shared across gems, be sure that files you load
  107. # through a railtie have unique names.
  108. #
  109. # == Application and Engine
  110. #
  111. # An engine is nothing more than a railtie with some initializers already set. And since
  112. # <tt>Rails::Application</tt> is an engine, the same configuration described here can be
  113. # used in both.
  114. #
  115. # Be sure to look at the documentation of those specific classes for more information.
  116. class Railtie
  117. autoload :Configuration, "rails/railtie/configuration"
  118. include Initializable
  119. ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application)
  120. class << self
  121. private :new
  122. delegate :config, to: :instance
  123. def subclasses
  124. @subclasses ||= []
  125. end
  126. def inherited(base)
  127. unless base.abstract_railtie?
  128. subclasses << base
  129. end
  130. end
  131. def rake_tasks(&blk)
  132. register_block_for(:rake_tasks, &blk)
  133. end
  134. def console(&blk)
  135. register_block_for(:load_console, &blk)
  136. end
  137. def runner(&blk)
  138. register_block_for(:runner, &blk)
  139. end
  140. def generators(&blk)
  141. register_block_for(:generators, &blk)
  142. end
  143. def abstract_railtie?
  144. ABSTRACT_RAILTIES.include?(name)
  145. end
  146. def railtie_name(name = nil)
  147. @railtie_name = name.to_s if name
  148. @railtie_name ||= generate_railtie_name(self.name)
  149. end
  150. # Since Rails::Railtie cannot be instantiated, any methods that call
  151. # +instance+ are intended to be called only on subclasses of a Railtie.
  152. def instance
  153. @instance ||= new
  154. end
  155. # Allows you to configure the railtie. This is the same method seen in
  156. # Railtie::Configurable, but this module is no longer required for all
  157. # subclasses of Railtie so we provide the class method here.
  158. def configure(&block)
  159. instance.configure(&block)
  160. end
  161. private
  162. def generate_railtie_name(string)
  163. ActiveSupport::Inflector.underscore(string).tr("/", "_")
  164. end
  165. def respond_to_missing?(name, _)
  166. instance.respond_to?(name) || super
  167. end
  168. # If the class method does not have a method, then send the method call
  169. # to the Railtie instance.
  170. def method_missing(name, *args, &block)
  171. if instance.respond_to?(name)
  172. instance.public_send(name, *args, &block)
  173. else
  174. super
  175. end
  176. end
  177. ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
  178. # receives an instance variable identifier, set the variable value if is
  179. # blank and append given block to value, which will be used later in
  180. # `#each_registered_block(type, &block)`
  181. def register_block_for(type, &blk)
  182. var_name = "@#{type}"
  183. blocks = instance_variable_defined?(var_name) ? instance_variable_get(var_name) : instance_variable_set(var_name, [])
  184. blocks << blk if blk
  185. blocks
  186. end
  187. end
  188. delegate :railtie_name, to: :class
  189. def initialize #:nodoc:
  190. if self.class.abstract_railtie?
  191. raise "#{self.class.name} is abstract, you cannot instantiate it directly."
  192. end
  193. end
  194. def configure(&block) #:nodoc:
  195. instance_eval(&block)
  196. end
  197. # This is used to create the <tt>config</tt> object on Railties, an instance of
  198. # Railtie::Configuration, that is used by Railties and Application to store
  199. # related configuration.
  200. def config
  201. @config ||= Railtie::Configuration.new
  202. end
  203. def railtie_namespace #:nodoc:
  204. @railtie_namespace ||= self.class.module_parents.detect { |n| n.respond_to?(:railtie_namespace) }
  205. end
  206. protected
  207. def run_console_blocks(app) #:nodoc:
  208. each_registered_block(:console) { |block| block.call(app) }
  209. end
  210. def run_generators_blocks(app) #:nodoc:
  211. each_registered_block(:generators) { |block| block.call(app) }
  212. end
  213. def run_runner_blocks(app) #:nodoc:
  214. each_registered_block(:runner) { |block| block.call(app) }
  215. end
  216. def run_tasks_blocks(app) #:nodoc:
  217. extend Rake::DSL
  218. each_registered_block(:rake_tasks) { |block| instance_exec(app, &block) }
  219. end
  220. private
  221. # run `&block` in every registered block in `#register_block_for`
  222. def each_registered_block(type, &block)
  223. klass = self.class
  224. while klass.respond_to?(type)
  225. klass.public_send(type).each(&block)
  226. klass = klass.superclass
  227. end
  228. end
  229. end
  230. end

lib/rails/railtie/configurable.rb

0.0% lines covered

27 relevant lines. 0 lines covered and 27 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/concern"
  3. module Rails
  4. class Railtie
  5. module Configurable
  6. extend ActiveSupport::Concern
  7. module ClassMethods
  8. delegate :config, to: :instance
  9. def inherited(base)
  10. raise "You cannot inherit from a #{superclass.name} child"
  11. end
  12. def instance
  13. @instance ||= new
  14. end
  15. def respond_to?(*args)
  16. super || instance.respond_to?(*args)
  17. end
  18. def configure(&block)
  19. class_eval(&block)
  20. end
  21. private
  22. def method_missing(*args, &block)
  23. instance.send(*args, &block)
  24. end
  25. end
  26. end
  27. end
  28. end

lib/rails/railtie/configuration.rb

0.0% lines covered

62 relevant lines. 0 lines covered and 62 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/configuration"
  3. require "active_support/core_ext/symbol/starts_ends_with"
  4. module Rails
  5. class Railtie
  6. class Configuration
  7. def initialize
  8. @@options ||= {}
  9. end
  10. # Expose the eager_load_namespaces at "module" level for convenience.
  11. def self.eager_load_namespaces #:nodoc:
  12. @@eager_load_namespaces ||= []
  13. end
  14. # All namespaces that are eager loaded
  15. def eager_load_namespaces
  16. @@eager_load_namespaces ||= []
  17. end
  18. # Add files that should be watched for change.
  19. def watchable_files
  20. @@watchable_files ||= []
  21. end
  22. # Add directories that should be watched for change.
  23. # The key of the hashes should be directories and the values should
  24. # be an array of extensions to match in each directory.
  25. def watchable_dirs
  26. @@watchable_dirs ||= {}
  27. end
  28. # This allows you to modify the application's middlewares from Engines.
  29. #
  30. # All operations you run on the app_middleware will be replayed on the
  31. # application once it is defined and the default_middlewares are
  32. # created
  33. def app_middleware
  34. @@app_middleware ||= Rails::Configuration::MiddlewareStackProxy.new
  35. end
  36. # This allows you to modify application's generators from Railties.
  37. #
  38. # Values set on app_generators will become defaults for application, unless
  39. # application overwrites them.
  40. def app_generators
  41. @@app_generators ||= Rails::Configuration::Generators.new
  42. yield(@@app_generators) if block_given?
  43. @@app_generators
  44. end
  45. # First configurable block to run. Called before any initializers are run.
  46. def before_configuration(&block)
  47. ActiveSupport.on_load(:before_configuration, yield: true, &block)
  48. end
  49. # Third configurable block to run. Does not run if +config.eager_load+
  50. # set to false.
  51. def before_eager_load(&block)
  52. ActiveSupport.on_load(:before_eager_load, yield: true, &block)
  53. end
  54. # Second configurable block to run. Called before frameworks initialize.
  55. def before_initialize(&block)
  56. ActiveSupport.on_load(:before_initialize, yield: true, &block)
  57. end
  58. # Last configurable block to run. Called after frameworks initialize.
  59. def after_initialize(&block)
  60. ActiveSupport.on_load(:after_initialize, yield: true, &block)
  61. end
  62. # Array of callbacks defined by #to_prepare.
  63. def to_prepare_blocks
  64. @@to_prepare_blocks ||= []
  65. end
  66. # Defines generic callbacks to run before #after_initialize. Useful for
  67. # Rails::Railtie subclasses.
  68. def to_prepare(&blk)
  69. to_prepare_blocks << blk if blk
  70. end
  71. def respond_to?(name, include_private = false)
  72. super || @@options.key?(name.to_sym)
  73. end
  74. private
  75. def method_missing(name, *args, &blk)
  76. if name.end_with?("=")
  77. @@options[:"#{name[0..-2]}"] = args.first
  78. elsif @@options.key?(name)
  79. @@options[name]
  80. else
  81. super
  82. end
  83. end
  84. end
  85. end
  86. end

lib/rails/ruby_version_check.rb

0.0% lines covered

8 relevant lines. 0 lines covered and 8 lines missed.
    
  1. # frozen_string_literal: true
  2. if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5.0") && RUBY_ENGINE == "ruby"
  3. desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
  4. abort <<-end_message
  5. Rails 6 requires Ruby 2.5.0 or newer.
  6. You're running
  7. #{desc}
  8. Please upgrade to Ruby 2.5.0 or newer to continue.
  9. end_message
  10. end

lib/rails/secrets.rb

0.0% lines covered

81 relevant lines. 0 lines covered and 81 lines missed.
    
  1. # frozen_string_literal: true
  2. require "yaml"
  3. require "active_support/message_encryptor"
  4. module Rails
  5. # Greatly inspired by Ara T. Howard's magnificent sekrets gem. 😘
  6. class Secrets # :nodoc:
  7. class MissingKeyError < RuntimeError
  8. def initialize
  9. super(<<-end_of_message.squish)
  10. Missing encryption key to decrypt secrets with.
  11. Ask your team for your master key and put it in ENV["RAILS_MASTER_KEY"]
  12. end_of_message
  13. end
  14. end
  15. @cipher = "aes-128-gcm"
  16. @root = File # Wonky, but ensures `join` uses the current directory.
  17. class << self
  18. attr_writer :root
  19. def parse(paths, env:)
  20. paths.each_with_object(Hash.new) do |path, all_secrets|
  21. require "erb"
  22. secrets = YAML.load(ERB.new(preprocess(path)).result) || {}
  23. all_secrets.merge!(secrets["shared"].deep_symbolize_keys) if secrets["shared"]
  24. all_secrets.merge!(secrets[env].deep_symbolize_keys) if secrets[env]
  25. end
  26. end
  27. def key
  28. ENV["RAILS_MASTER_KEY"] || read_key_file || handle_missing_key
  29. end
  30. def encrypt(data)
  31. encryptor.encrypt_and_sign(data)
  32. end
  33. def decrypt(data)
  34. encryptor.decrypt_and_verify(data)
  35. end
  36. def read
  37. decrypt(IO.binread(path))
  38. end
  39. def write(contents)
  40. IO.binwrite("#{path}.tmp", encrypt(contents))
  41. FileUtils.mv("#{path}.tmp", path)
  42. end
  43. def read_for_editing(&block)
  44. writing(read, &block)
  45. end
  46. private
  47. def handle_missing_key
  48. raise MissingKeyError
  49. end
  50. def read_key_file
  51. if File.exist?(key_path)
  52. IO.binread(key_path).strip
  53. end
  54. end
  55. def key_path
  56. @root.join("config", "secrets.yml.key")
  57. end
  58. def path
  59. @root.join("config", "secrets.yml.enc").to_s
  60. end
  61. def preprocess(path)
  62. if path.end_with?(".enc")
  63. decrypt(IO.binread(path))
  64. else
  65. IO.read(path)
  66. end
  67. end
  68. def writing(contents)
  69. tmp_file = "#{File.basename(path)}.#{Process.pid}"
  70. tmp_path = File.join(Dir.tmpdir, tmp_file)
  71. IO.binwrite(tmp_path, contents)
  72. yield tmp_path
  73. updated_contents = IO.binread(tmp_path)
  74. write(updated_contents) if updated_contents != contents
  75. ensure
  76. FileUtils.rm(tmp_path) if File.exist?(tmp_path)
  77. end
  78. def encryptor
  79. @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: @cipher)
  80. end
  81. end
  82. end
  83. end

lib/rails/source_annotation_extractor.rb

39.68% lines covered

63 relevant lines. 25 lines covered and 38 lines missed.
    
  1. # frozen_string_literal: true
  2. 17 require "active_support/deprecation"
  3. 17 module Rails
  4. # Implements the logic behind <tt>Rails::Command::NotesCommand</tt>. See <tt>rails notes --help</tt> for usage information.
  5. #
  6. # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
  7. # represent the line where the annotation lives, its tag, and its text. Note
  8. # the filename is not stored.
  9. #
  10. # Annotations are looked for in comments and modulus whitespace they have to
  11. # start with the tag optionally followed by a colon. Everything up to the end
  12. # of the line (or closing ERB comment tag) is considered to be their text.
  13. 17 class SourceAnnotationExtractor
  14. 17 class Annotation < Struct.new(:line, :tag, :text)
  15. 17 def self.directories
  16. @@directories ||= %w(app config db lib test)
  17. end
  18. # Registers additional directories to be included
  19. # Rails::SourceAnnotationExtractor::Annotation.register_directories("spec", "another")
  20. 17 def self.register_directories(*dirs)
  21. directories.push(*dirs)
  22. end
  23. 17 def self.tags
  24. @@tags ||= %w(OPTIMIZE FIXME TODO)
  25. end
  26. # Registers additional tags
  27. # Rails::SourceAnnotationExtractor::Annotation.register_tags("TESTME", "DEPRECATEME")
  28. 17 def self.register_tags(*additional_tags)
  29. tags.push(*additional_tags)
  30. end
  31. 17 def self.extensions
  32. 51 @@extensions ||= {}
  33. end
  34. # Registers new Annotations File Extensions
  35. # Rails::SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
  36. 17 def self.register_extensions(*exts, &block)
  37. 51 extensions[/\.(#{exts.join("|")})$/] = block
  38. end
  39. 17 register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ }
  40. 17 register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
  41. 17 register_extensions("erb") { |tag| /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ }
  42. # Returns a representation of the annotation that looks like this:
  43. #
  44. # [126] [TODO] This algorithm is simple and clearly correct, make it faster.
  45. #
  46. # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
  47. # Otherwise the string contains just line and text.
  48. 17 def to_s(options = {})
  49. s = +"[#{line.to_s.rjust(options[:indent])}] "
  50. s << "[#{tag}] " if options[:tag]
  51. s << text
  52. end
  53. # Used in annotations.rake
  54. #:nodoc:
  55. 17 def self.notes_task_deprecation_warning
  56. 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")
  57. puts "\n"
  58. end
  59. end
  60. # Prints all annotations with tag +tag+ under the root directories +app+,
  61. # +config+, +db+, +lib+, and +test+ (recursively).
  62. #
  63. # If +tag+ is <tt>nil</tt>, annotations with either default or registered tags are printed.
  64. #
  65. # Specific directories can be explicitly set using the <tt>:dirs</tt> key in +options+.
  66. #
  67. # Rails::SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
  68. #
  69. # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+.
  70. #
  71. # See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
  72. #
  73. # This class method is the single entry point for the `rails notes` command.
  74. 17 def self.enumerate(tag = nil, options = {})
  75. tag ||= Annotation.tags.join("|")
  76. extractor = new(tag)
  77. dirs = options.delete(:dirs) || Annotation.directories
  78. extractor.display(extractor.find(dirs), options)
  79. end
  80. 17 attr_reader :tag
  81. 17 def initialize(tag)
  82. @tag = tag
  83. end
  84. # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
  85. # with their annotations.
  86. 17 def find(dirs)
  87. dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
  88. end
  89. # Returns a hash that maps filenames under +dir+ (recursively) to arrays
  90. # with their annotations. Files with extensions registered in
  91. # <tt>Rails::SourceAnnotationExtractor::Annotation.extensions</tt> are
  92. # taken into account. Only files with annotations are included.
  93. 17 def find_in(dir)
  94. results = {}
  95. Dir.glob("#{dir}/*") do |item|
  96. next if File.basename(item).start_with?(".")
  97. if File.directory?(item)
  98. results.update(find_in(item))
  99. else
  100. extension = Annotation.extensions.detect do |regexp, _block|
  101. regexp.match(item)
  102. end
  103. if extension
  104. pattern = extension.last.call(tag)
  105. results.update(extract_annotations_from(item, pattern)) if pattern
  106. end
  107. end
  108. end
  109. results
  110. end
  111. # If +file+ is the filename of a file that contains annotations this method returns
  112. # a hash with a single entry that maps +file+ to an array of its annotations.
  113. # Otherwise it returns an empty hash.
  114. 17 def extract_annotations_from(file, pattern)
  115. lineno = 0
  116. result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line|
  117. lineno += 1
  118. next list unless line =~ pattern
  119. list << Annotation.new(lineno, $1, $2)
  120. end
  121. result.empty? ? {} : { file => result }
  122. end
  123. # Prints the mapping from filenames to annotations in +results+ ordered by filename.
  124. # The +options+ hash is passed to each annotation's +to_s+.
  125. 17 def display(results, options = {})
  126. options[:indent] = results.flat_map { |f, a| a.map(&:line) }.max.to_s.size
  127. results.keys.sort.each do |file|
  128. puts "#{file}:"
  129. results[file].each do |note|
  130. puts " * #{note.to_s(options)}"
  131. end
  132. puts
  133. end
  134. end
  135. end
  136. end
  137. # Remove this deprecated class in the next minor version
  138. #:nodoc:
  139. 17 SourceAnnotationExtractor = ActiveSupport::Deprecation::DeprecatedConstantProxy.
  140. new("SourceAnnotationExtractor", "Rails::SourceAnnotationExtractor")

lib/rails/tasks.rb

0.0% lines covered

19 relevant lines. 0 lines covered and 19 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rake"
  3. # Load Rails Rakefile extensions
  4. %w(
  5. annotations
  6. dev
  7. framework
  8. initializers
  9. log
  10. middleware
  11. misc
  12. restart
  13. routes
  14. tmp
  15. yarn
  16. zeitwerk
  17. ).tap { |arr|
  18. arr << "statistics" if Rake.application.current_scope.empty?
  19. }.each do |task|
  20. load "rails/tasks/#{task}.rake"
  21. end

lib/rails/test_help.rb

0.0% lines covered

36 relevant lines. 0 lines covered and 36 lines missed.
    
  1. # frozen_string_literal: true
  2. # Make double-sure the RAILS_ENV is not set to production,
  3. # so fixtures aren't loaded into that environment
  4. abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
  5. require "active_support/test_case"
  6. require "action_controller"
  7. require "action_controller/test_case"
  8. require "action_dispatch/testing/integration"
  9. require "rails/generators/test_case"
  10. require "active_support/testing/autorun"
  11. if defined?(ActiveRecord::Base)
  12. begin
  13. ActiveRecord::Migration.maintain_test_schema!
  14. rescue ActiveRecord::PendingMigrationError => e
  15. puts e.to_s.strip
  16. exit 1
  17. end
  18. ActiveSupport.on_load(:active_support_test_case) do
  19. include ActiveRecord::TestDatabases
  20. include ActiveRecord::TestFixtures
  21. self.fixture_path = "#{Rails.root}/test/fixtures/"
  22. self.file_fixture_path = fixture_path + "files"
  23. end
  24. ActiveSupport.on_load(:action_dispatch_integration_test) do
  25. self.fixture_path = ActiveSupport::TestCase.fixture_path
  26. end
  27. end
  28. # :enddoc:
  29. ActiveSupport.on_load(:action_controller_test_case) do
  30. def before_setup # :nodoc:
  31. @routes = Rails.application.routes
  32. super
  33. end
  34. end
  35. ActiveSupport.on_load(:action_dispatch_integration_test) do
  36. def before_setup # :nodoc:
  37. @routes = Rails.application.routes
  38. super
  39. end
  40. end

lib/rails/test_unit/line_filtering.rb

66.67% lines covered

6 relevant lines. 4 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 17 require "rails/test_unit/runner"
  3. 17 module Rails
  4. 17 module LineFiltering # :nodoc:
  5. 17 def run(reporter, options = {})
  6. options[:filter] = Rails::TestUnit::Runner.compose_filter(self, options[:filter])
  7. super
  8. end
  9. end
  10. end

lib/rails/test_unit/railtie.rb

71.43% lines covered

14 relevant lines. 10 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 17 require "rails/test_unit/line_filtering"
  3. 17 if defined?(Rake.application) && Rake.application.top_level_tasks.grep(/^(default$|test(:|$))/).any?
  4. ENV["RAILS_ENV"] ||= Rake.application.options.show_tasks ? "development" : "test"
  5. end
  6. 17 module Rails
  7. 17 class TestUnitRailtie < Rails::Railtie
  8. 17 config.app_generators do |c|
  9. 17 c.test_framework :test_unit, fixture: true,
  10. fixture_replacement: nil
  11. 17 c.integration_tool :test_unit
  12. 17 c.system_tests :test_unit
  13. end
  14. 17 initializer "test_unit.line_filtering" do
  15. ActiveSupport.on_load(:active_support_test_case) {
  16. ActiveSupport::TestCase.extend Rails::LineFiltering
  17. }
  18. end
  19. 17 rake_tasks do
  20. load "rails/test_unit/testing.rake"
  21. end
  22. end
  23. end

lib/rails/test_unit/reporter.rb

0.0% lines covered

91 relevant lines. 0 lines covered and 91 lines missed.
    
  1. # frozen_string_literal: true
  2. require "active_support/core_ext/class/attribute"
  3. require "minitest"
  4. module Rails
  5. class TestUnitReporter < Minitest::StatisticsReporter
  6. class_attribute :app_root
  7. class_attribute :executable, default: "rails test"
  8. def record(result)
  9. super
  10. if options[:verbose]
  11. io.puts color_output(format_line(result), by: result)
  12. else
  13. io.print color_output(result.result_code, by: result)
  14. end
  15. if output_inline? && result.failure && (!result.skipped? || options[:verbose])
  16. io.puts
  17. io.puts
  18. io.puts color_output(result, by: result)
  19. io.puts
  20. io.puts format_rerun_snippet(result)
  21. io.puts
  22. end
  23. if fail_fast? && result.failure && !result.skipped?
  24. raise Interrupt
  25. end
  26. end
  27. def report
  28. return if output_inline? || filtered_results.empty?
  29. io.puts
  30. io.puts "Failed tests:"
  31. io.puts
  32. io.puts aggregated_results
  33. end
  34. def aggregated_results # :nodoc:
  35. filtered_results.map { |result| format_rerun_snippet(result) }.join "\n"
  36. end
  37. def filtered_results
  38. if options[:verbose]
  39. results
  40. else
  41. results.reject(&:skipped?)
  42. end
  43. end
  44. def relative_path_for(file)
  45. file.sub(/^#{app_root}\/?/, "")
  46. end
  47. private
  48. def output_inline?
  49. options[:output_inline]
  50. end
  51. def fail_fast?
  52. options[:fail_fast]
  53. end
  54. def format_line(result)
  55. klass = result.respond_to?(:klass) ? result.klass : result.class
  56. "%s#%s = %.2f s = %s" % [klass, result.name, result.time, result.result_code]
  57. end
  58. def format_rerun_snippet(result)
  59. location, line = if result.respond_to?(:source_location)
  60. result.source_location
  61. else
  62. result.method(result.name).source_location
  63. end
  64. "#{executable} #{relative_path_for(location)}:#{line}"
  65. end
  66. def app_root
  67. @app_root ||= self.class.app_root ||
  68. if defined?(ENGINE_ROOT)
  69. ENGINE_ROOT
  70. elsif Rails.respond_to?(:root)
  71. Rails.root
  72. end
  73. end
  74. def colored_output?
  75. options[:color] && io.respond_to?(:tty?) && io.tty?
  76. end
  77. codes = { red: 31, green: 32, yellow: 33 }
  78. COLOR_BY_RESULT_CODE = {
  79. "." => codes[:green],
  80. "E" => codes[:red],
  81. "F" => codes[:red],
  82. "S" => codes[:yellow]
  83. }
  84. def color_output(string, by:)
  85. if colored_output?
  86. "\e[#{COLOR_BY_RESULT_CODE[by.result_code]}m#{string}\e[0m"
  87. else
  88. string
  89. end
  90. end
  91. end
  92. end

lib/rails/test_unit/runner.rb

36.67% lines covered

90 relevant lines. 33 lines covered and 57 lines missed.
    
  1. # frozen_string_literal: true
  2. 17 require "shellwords"
  3. 17 require "method_source"
  4. 17 require "rake/file_list"
  5. 17 require "active_support/core_ext/module/attribute_accessors"
  6. 17 module Rails
  7. 17 module TestUnit
  8. 17 class Runner
  9. 17 mattr_reader :filters, default: []
  10. 17 class << self
  11. 17 def attach_before_load_options(opts)
  12. opts.on("--warnings", "-w", "Run with Ruby warnings enabled") { }
  13. opts.on("-e", "--environment ENV", "Run tests in the ENV environment") { }
  14. end
  15. 17 def parse_options(argv)
  16. # Perform manual parsing and cleanup since option parser raises on unknown options.
  17. env_index = argv.index("--environment") || argv.index("-e")
  18. if env_index
  19. argv.delete_at(env_index)
  20. environment = argv.delete_at(env_index).strip
  21. end
  22. ENV["RAILS_ENV"] = environment || "test"
  23. w_index = argv.index("--warnings") || argv.index("-w")
  24. $VERBOSE = argv.delete_at(w_index) if w_index
  25. end
  26. 17 def rake_run(argv = [])
  27. ARGV.replace Shellwords.split(ENV["TESTOPTS"] || "")
  28. run(argv)
  29. end
  30. 17 def run(argv = [])
  31. load_tests(argv)
  32. require "active_support/testing/autorun"
  33. end
  34. 17 def load_tests(argv)
  35. patterns = extract_filters(argv)
  36. tests = Rake::FileList[patterns.any? ? patterns : default_test_glob]
  37. tests.exclude(default_test_exclude_glob) if patterns.empty?
  38. tests.to_a.each { |path| require File.expand_path(path) }
  39. end
  40. 17 def compose_filter(runnable, filter)
  41. if filters.any? { |_, lines| lines.any? }
  42. CompositeFilter.new(runnable, filter, filters)
  43. else
  44. filter
  45. end
  46. end
  47. 17 private
  48. 17 def extract_filters(argv)
  49. # Extract absolute and relative paths but skip -n /.*/ regexp filters.
  50. argv.select { |arg| path_argument?(arg) && !regexp_filter?(arg) }.map do |path|
  51. path = path.tr("\\", "/")
  52. case
  53. when /(:\d+)+$/.match?(path)
  54. file, *lines = path.split(":")
  55. filters << [ file, lines ]
  56. file
  57. when Dir.exist?(path)
  58. "#{path}/**/*_test.rb"
  59. else
  60. filters << [ path, [] ]
  61. path
  62. end
  63. end
  64. end
  65. 17 def default_test_glob
  66. ENV["DEFAULT_TEST"] || "test/**/*_test.rb"
  67. end
  68. 17 def default_test_exclude_glob
  69. ENV["DEFAULT_TEST_EXCLUDE"] || "test/{system,dummy}/**/*_test.rb"
  70. end
  71. 17 def regexp_filter?(arg)
  72. arg.start_with?("/") && arg.end_with?("/")
  73. end
  74. 17 def path_argument?(arg)
  75. %r"^[/\\]?\w+[/\\]".match?(arg)
  76. end
  77. end
  78. end
  79. 17 class CompositeFilter # :nodoc:
  80. 17 attr_reader :named_filter
  81. 17 def initialize(runnable, filter, patterns)
  82. @runnable = runnable
  83. @named_filter = derive_named_filter(filter)
  84. @filters = [ @named_filter, *derive_line_filters(patterns) ].compact
  85. end
  86. # minitest uses === to find matching filters.
  87. 17 def ===(method)
  88. @filters.any? { |filter| filter === method }
  89. end
  90. 17 private
  91. 17 def derive_named_filter(filter)
  92. if filter.respond_to?(:named_filter)
  93. filter.named_filter
  94. elsif filter =~ %r%/(.*)/% # Regexp filtering copied from minitest.
  95. Regexp.new $1
  96. elsif filter.is_a?(String)
  97. filter
  98. end
  99. end
  100. 17 def derive_line_filters(patterns)
  101. patterns.flat_map do |file, lines|
  102. if lines.empty?
  103. Filter.new(@runnable, file, nil) if file
  104. else
  105. lines.map { |line| Filter.new(@runnable, file, line) }
  106. end
  107. end
  108. end
  109. end
  110. 17 class Filter # :nodoc:
  111. 17 def initialize(runnable, file, line)
  112. @runnable, @file = runnable, File.expand_path(file)
  113. @line = line.to_i if line
  114. end
  115. 17 def ===(method)
  116. return unless @runnable.method_defined?(method)
  117. if @line
  118. test_file, test_range = definition_for(@runnable.instance_method(method))
  119. test_file == @file && test_range.include?(@line)
  120. else
  121. @runnable.instance_method(method).source_location.first == @file
  122. end
  123. end
  124. 17 private
  125. 17 def definition_for(method)
  126. file, start_line = method.source_location
  127. end_line = method.source.count("\n") + start_line - 1
  128. return file, start_line..end_line
  129. end
  130. end
  131. end
  132. end

lib/rails/version.rb

0.0% lines covered

6 relevant lines. 0 lines covered and 6 lines missed.
    
  1. # frozen_string_literal: true
  2. require_relative "gem_version"
  3. module Rails
  4. # Returns the version of the currently loaded Rails as a string.
  5. def self.version
  6. VERSION::STRING
  7. end
  8. end

lib/rails/welcome_controller.rb

0.0% lines covered

6 relevant lines. 0 lines covered and 6 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/application_controller"
  3. class Rails::WelcomeController < Rails::ApplicationController # :nodoc:
  4. layout false
  5. def index
  6. end
  7. end