diff options
Diffstat (limited to 'railties')
69 files changed, 813 insertions, 425 deletions
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 3afadc8cba..5463b05b60 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,158 +1,8 @@ -## Rails 5.1.0.beta1 (February 23, 2017) ## +* Namespace error pages' CSS selectors to stop the styles from bleeding into other pages + when using Turbolinks. + + *Jan Krutisch* -* Fix running multiple tests in one `rake` command + - e.g. `bin/rake test:models test:controllers` - - *Dominic Cleal* - -* Add option to configure Ruby's warning behaviour to test runner. - - *Yuji Yaginuma* - -* Initialize git repo when generating new app, if option `--skip-git` - is not provided. - - *Dino Maric* - -* Install Byebug gem as default in Windows (mingw and x64_mingw) platform. - - *Junichi Ito* - -* Make every Rails command work within engines. - - *Sean Collins*, *Yuji Yaginuma* - -* Don't generate HTML/ERB templates for scaffold controller with `--api` flag. - - Fixes #27591. - - *Prathamesh Sonpatki* - -* Make `Rails.env` fall back to `development` when `RAILS_ENV` and `RACK_ENV` is an empty string. - - *Daniel Deng* - -* Remove deprecated `CONTROLLER` environment variable for `routes` task. - - *Rafael Mendonça França* - -* Remove deprecated tasks: `rails:update`, `rails:template`, `rails:template:copy`, - `rails:update:configs` and `rails:update:bin`. - - *Rafael Mendonça França* - -* Remove deprecated file `rails/rack/debugger`. - - *Rafael Mendonça França* - -* Remove deprecated `config.serve_static_files`. - - *Rafael Mendonça França* - -* Remove deprecated `config.static_cache_control`. - - *Rafael Mendonça França* - -* The `log:clear` task clear all environments log files by default. - - *Yuji Yaginuma* - -* Add Webpack support in new apps via the --webpack option, which will delegate to the rails/webpacker gem. - - To generate a new app that has Webpack dependencies configured and binstubs for webpack and webpack-watcher: - - `rails new myapp --webpack` - - To generate a new app that has Webpack + React configured and an example intalled: - - `rails new myapp --webpack=react` - - *DHH* - -* Add Yarn support in new apps with a yarn binstub and package.json. Skippable via --skip-yarn option. - - *Liceth Ovalles*, *Guillermo Iguaran*, *DHH* - -* Removed jquery-rails from default stack, instead rails-ujs that is shipped - with Action View is included as default UJS adapter. - - *Guillermo Iguaran* - -* The config file `secrets.yml` is now loaded in with all keys as symbols. - This allows secrets files to contain more complex information without all - child keys being strings while parent keys are symbols. - - *Isaac Sloan* - -* Add `:skip_sprockets` to `Rails::PluginBuilder::PASSTHROUGH_OPTIONS` - - *Tsukuru Tanimichi* - -* Allow the use of listen's 3.1.x branch - - *Esteban Santana Santana* - -* Run `Minitest.after_run` hooks when running `rails test`. - - *Michael Grosser* - -* Run `before_configuration` callbacks as soon as application constant - inherits from `Rails::Application`. - - Fixes #19880. - - *Yuji Yaginuma* - -* A generated app should not include Uglifier with `--skip-javascript` option. - - *Ben Pickles* - -* Set session store to cookie store internally and remove the initializer from - the generated app. - - *Prathamesh Sonpatki* - -* Set the server host using the `HOST` environment variable. - - *mahnunchik* - -* Add public API to register new folders for `rake notes`: - - config.annotations.register_directories('spec', 'features') - - *John Meehan* - -* Display name of the class defining the initializer along with the initializer - name in the output of `rails initializers`. - - Before: - disable_dependency_loading - - After: - DemoApp::Application.disable_dependency_loading - - *ta1kt0me* - -* Do not run `bundle install` when generating a new plugin. - - Since bundler 1.12.0, the gemspec is validated so the `bundle install` - command will fail just after the gem is created causing confusion to the - users. This change was a bug fix to correctly validate gemspecs. - - *Rafael Mendonça França* - -* Default `config.assets.quiet = true` in the development environment. Suppress - logging of assets requests by default. - - *Kevin McPhillips* - -* Ensure `/rails/info` routes match in development for apps with a catch-all globbing route. - - *Nicholas Firth-McCoy* - -* Added a shared section to `config/secrets.yml` that will be loaded for all environments. - - *DHH* - -Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/railties/CHANGELOG.md) for previous changes. +Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/railties/CHANGELOG.md) for previous changes. diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index 00add5829d..6d8bf28943 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -87,8 +87,8 @@ module Rails # groups assets: [:development, :test] # # # Returns - # # => [:default, :development, :assets] for Rails.env == "development" - # # => [:default, :production] for Rails.env == "production" + # # => [:default, "development", :assets] for Rails.env == "development" + # # => [:default, "production"] for Rails.env == "production" def groups(*groups) hash = groups.extract_options! env = Rails.env diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 89f7b5991f..f8a923141d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -386,7 +386,9 @@ module Rails def secrets @secrets ||= begin secrets = ActiveSupport::OrderedOptions.new - secrets.merge! Rails::Secrets.parse(config.paths["config/secrets"].existent, env: Rails.env) + files = config.paths["config/secrets"].existent + files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets + secrets.merge! Rails::Secrets.parse(files, env: Rails.env) # Fallback to config.secret_key_base if secrets.secret_key_base isn't set secrets.secret_key_base ||= config.secret_key_base diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index 4223c38146..dc0491035d 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -81,7 +81,6 @@ INFO initializer :set_secrets_root, group: :all do Rails::Secrets.root = root - Rails::Secrets.read_encrypted_secrets = config.read_encrypted_secrets end end end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index b0592151b7..7c49fabba5 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -14,9 +14,8 @@ module Rails :ssl_options, :public_file_server, :session_options, :time_zone, :reload_classes_only_on_change, :beginning_of_week, :filter_redirect, :x, :enable_dependency_loading, - :read_encrypted_secrets + :read_encrypted_secrets, :log_level - attr_writer :log_level attr_reader :encoding, :api_only def initialize(*) @@ -35,7 +34,7 @@ module Rails @session_store = nil @time_zone = "UTC" @beginning_of_week = :monday - @log_level = nil + @log_level = :debug @generators = app_generators @cache_store = [ :file_store, "#{root}/tmp/cache/" ] @railties_order = [:all] @@ -55,6 +54,37 @@ module Rails @read_encrypted_secrets = false end + def load_defaults(target_version) + case target_version.to_s + when "5.0" + if respond_to?(:action_controller) + action_controller.per_form_csrf_tokens = true + action_controller.forgery_protection_origin_check = true + end + + ActiveSupport.to_time_preserves_timezone = true + + if respond_to?(:active_record) + active_record.belongs_to_required_by_default = true + end + + self.ssl_options = { hsts: { subdomains: true } } + + when "5.1" + load_defaults "5.0" + + if respond_to?(:assets) + assets.unknown_asset_fallback = false + end + + when "5.2" + load_defaults "5.1" + + else + raise "Unknown version #{target_version.to_s.inspect}" + end + end + def encoding=(value) @encoding = value silence_warnings do @@ -121,10 +151,6 @@ module Rails raise e, "Cannot load `Rails.application.database_configuration`:\n#{e.message}", e.backtrace end - def log_level - @log_level ||= (Rails.env.production? ? :info : :debug) - end - def colorize_logging ActiveSupport::LogSubscriber.colorize_logging end diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb index 5c833e12ba..3bd18ebfb5 100644 --- a/railties/lib/rails/backtrace_cleaner.rb +++ b/railties/lib/rails/backtrace_cleaner.rb @@ -16,7 +16,7 @@ module Rails add_filter { |line| line.sub(DOT_SLASH, SLASH) } # for tests add_gem_filters - add_silencer { |line| line !~ APP_DIRS_PATTERN } + add_silencer { |line| !APP_DIRS_PATTERN.match?(line) } end private diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb index 9c4bd16aad..70dce268f1 100644 --- a/railties/lib/rails/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb @@ -7,7 +7,8 @@ class CodeStatistics #:nodoc: "Model tests", "Mailer tests", "Job tests", - "Integration tests"] + "Integration tests", + "System tests"] HEADERS = { lines: " Lines", code_lines: " LOC", classes: "Classes", methods: "Methods" } diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb index db20c71861..4f074df473 100644 --- a/railties/lib/rails/command/base.rb +++ b/railties/lib/rails/command/base.rb @@ -64,7 +64,7 @@ module Rails end def printing_commands - namespace.sub(/^rails:/, "") + namespaced_commands end def executable @@ -135,6 +135,12 @@ module Rails def command_root_namespace (namespace.split(":") - %w( rails )).first end + + def namespaced_commands + commands.keys.map do |key| + key == command_root_namespace ? key : "#{command_root_namespace}:#{key}" + end + end end def help diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb index 588fb06b15..5bfbe58d97 100644 --- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb +++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb @@ -140,7 +140,7 @@ module Rails class_option :mode, enum: %w( html list line column ), type: :string, desc: "Automatically put the sqlite3 database in the specified mode (html, list, line, column)." - class_option :header, type: :string + class_option :header, type: :boolean class_option :environment, aliases: "-e", type: :string, desc: "Specifies the environment to run this console under (test/development/production)." diff --git a/railties/lib/rails/commands/destroy/destroy_command.rb b/railties/lib/rails/commands/destroy/destroy_command.rb index 5b552b2070..281732a936 100644 --- a/railties/lib/rails/commands/destroy/destroy_command.rb +++ b/railties/lib/rails/commands/destroy/destroy_command.rb @@ -3,8 +3,13 @@ require "rails/generators" module Rails module Command class DestroyCommand < Base # :nodoc: - def help - Rails::Generators.help self.class.command_name + no_commands do + def help + require_application_and_environment! + load_generators + + Rails::Generators.help self.class.command_name + end end def perform(*) @@ -12,9 +17,9 @@ module Rails return help unless generator require_application_and_environment! - Rails.application.load_generators + load_generators - Rails::Generators.invoke generator, args, behavior: :revoke, destination_root: Rails.root + Rails::Generators.invoke generator, args, behavior: :revoke, destination_root: Rails::Command.root end end end diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb index 2718b453a8..9dd7ad1012 100644 --- a/railties/lib/rails/commands/generate/generate_command.rb +++ b/railties/lib/rails/commands/generate/generate_command.rb @@ -3,11 +3,13 @@ require "rails/generators" module Rails module Command class GenerateCommand < Base # :nodoc: - def help - require_application_and_environment! - load_generators + no_commands do + def help + require_application_and_environment! + load_generators - Rails::Generators.help self.class.command_name + Rails::Generators.help self.class.command_name + end end def perform(*) diff --git a/railties/lib/rails/commands/new/new_command.rb b/railties/lib/rails/commands/new/new_command.rb index 74d1fa5021..207dd5d995 100644 --- a/railties/lib/rails/commands/new/new_command.rb +++ b/railties/lib/rails/commands/new/new_command.rb @@ -1,8 +1,10 @@ module Rails module Command class NewCommand < Base # :nodoc: - def help - Rails::Command.invoke :application, [ "--help" ] + no_commands do + def help + Rails::Command.invoke :application, [ "--help" ] + end end def perform(*) diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb index 4989a7837d..6864a9726b 100644 --- a/railties/lib/rails/commands/runner/runner_command.rb +++ b/railties/lib/rails/commands/runner/runner_command.rb @@ -5,16 +5,18 @@ module Rails default: Rails::Command.environment.dup, desc: "The environment for the runner to operate under (test/development/production)" - def help - super - puts self.class.desc + no_commands do + def help + super + puts self.class.desc + end end def self.banner(*) "#{super} [<'Some.ruby(code)'> | <filename.rb>]" end - def perform(code_or_file = nil, *file_argv) + def perform(code_or_file = nil, *command_argv) unless code_or_file help exit 1 @@ -25,9 +27,10 @@ module Rails require_application_and_environment! Rails.application.load_runner + ARGV.replace(command_argv) + if File.exist?(code_or_file) $0 = code_or_file - ARGV.replace(file_argv) Kernel.load code_or_file else begin diff --git a/railties/lib/rails/commands/secrets/USAGE b/railties/lib/rails/commands/secrets/USAGE index 4b7deb4e2a..96e322fe91 100644 --- a/railties/lib/rails/commands/secrets/USAGE +++ b/railties/lib/rails/commands/secrets/USAGE @@ -40,6 +40,14 @@ be encrypted. A `shared:` top level key is also supported such that any keys there is merged into the other environments. +Additionally, Rails won't read encrypted secrets out of the box even if you have +the key. Add this: + + config.read_encrypted_secrets = true + +to the environment you'd like to read encrypted secrets. `bin/rails secrets:setup` +inserts this into the production environment by default. + === Editing Secrets After `bin/rails secrets:setup`, run `bin/rails secrets:edit`. diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb index 3ba8c0c85b..03a640bd65 100644 --- a/railties/lib/rails/commands/secrets/secrets_command.rb +++ b/railties/lib/rails/commands/secrets/secrets_command.rb @@ -4,10 +4,12 @@ require "rails/secrets" module Rails module Command class SecretsCommand < Rails::Command::Base # :nodoc: - def help - say "Usage:\n #{self.class.banner}" - say "" - say self.class.desc + no_commands do + def help + say "Usage:\n #{self.class.banner}" + say "" + say self.class.desc + end end def setup @@ -18,16 +20,27 @@ module Rails end def edit + if ENV["EDITOR"].to_s.empty? + say "No $EDITOR to open decrypted secrets in. Assign one like this:" + say "" + say %(EDITOR="mate --wait" bin/rails secrets:edit) + say "" + say "For editors that fork and exit immediately, it's important to pass a wait flag," + say "otherwise the secrets will be saved immediately with no chance to edit." + + return + end + require_application_and_environment! Rails::Secrets.read_for_editing do |tmp_path| - puts "Waiting for secrets file to be saved. Abort with Ctrl-C." + say "Waiting for secrets file to be saved. Abort with Ctrl-C." system("\$EDITOR #{tmp_path}") end - puts "New secrets encrypted and saved." + say "New secrets encrypted and saved." rescue Interrupt - puts "Aborted changing encrypted secrets: nothing saved." + say "Aborted changing encrypted secrets: nothing saved." rescue Rails::Secrets::MissingKeyError => error say error.message end diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index 7e8c86fb49..278fe63c51 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -188,10 +188,12 @@ module Rails end def host - unless (default_host = options[:binding]) + if options[:binding] + options[:binding] + else default_host = environment == "development" ? "localhost" : "0.0.0.0" + ENV.fetch("HOST", default_host) end - ENV.fetch("HOST", default_host) end def environment diff --git a/railties/lib/rails/commands/test/test_command.rb b/railties/lib/rails/commands/test/test_command.rb index 629fb5b425..65e16900ba 100644 --- a/railties/lib/rails/commands/test/test_command.rb +++ b/railties/lib/rails/commands/test/test_command.rb @@ -4,8 +4,10 @@ require "rails/test_unit/minitest_plugin" module Rails module Command class TestCommand < Base # :nodoc: - def help - perform # Hand over help printing to minitest. + no_commands do + def help + perform # Hand over help printing to minitest. + end end def perform(*) diff --git a/railties/lib/rails/engine/updater.rb b/railties/lib/rails/engine/updater.rb new file mode 100644 index 0000000000..2ecf994a5c --- /dev/null +++ b/railties/lib/rails/engine/updater.rb @@ -0,0 +1,19 @@ +require "rails/generators" +require "rails/generators/rails/plugin/plugin_generator" + +module Rails + class Engine + class Updater + class << self + def generator + @generator ||= Rails::Generators::PluginGenerator.new ["plugin"], + { engine: true }, destination_root: ENGINE_ROOT + end + + def run(action) + generator.send(action) + end + end + end + end +end diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb index 3174ffb0dc..7bacf2e0ba 100644 --- a/railties/lib/rails/gem_version.rb +++ b/railties/lib/rails/gem_version.rb @@ -6,9 +6,9 @@ module Rails module VERSION MAJOR = 5 - MINOR = 1 + MINOR = 2 TINY = 0 - PRE = "beta1" + PRE = "alpha" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 56e286f259..c715e5ac9f 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -13,6 +13,7 @@ module Rails DATABASES = %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver ) JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc ) DATABASES.concat(JDBC_DATABASES) + WEBPACKS = %w( react vue angular ) attr_accessor :rails_template add_shebang_option! @@ -30,11 +31,8 @@ module Rails class_option :database, type: :string, aliases: "-d", default: "sqlite3", desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})" - class_option :javascript, type: :string, aliases: "-j", - desc: "Preconfigure for selected JavaScript library" - class_option :webpack, type: :string, default: nil, - desc: "Preconfigure for app-like JavaScript with Webpack" + desc: "Preconfigure for app-like JavaScript with Webpack (options: #{WEBPACKS.join('/')})" class_option :skip_yarn, type: :boolean, default: false, desc: "Don't use Yarn for managing JavaScript dependencies" @@ -246,6 +244,7 @@ module Rails def rails_gemfile_entry dev_edge_common = [ + GemfileEntry.github("arel", "rails/arel"), ] if options.dev? [ @@ -280,7 +279,7 @@ module Rails case options[:database] when "mysql" then ["mysql2", [">= 0.3.18", "< 0.5"]] when "postgresql" then ["pg", ["~> 0.18"]] - when "oracle" then ["ruby-oci8", nil] + when "oracle" then ["activerecord-oracle_enhanced-adapter", nil] when "frontbase" then ["ruby-frontbase", nil] when "sqlserver" then ["activerecord-sqlserver-adapter", nil] when "jdbcmysql" then ["activerecord-jdbcmysql-adapter", nil] @@ -296,7 +295,6 @@ module Rails case options[:database] when "postgresql" then options[:database].replace "jdbcpostgresql" when "mysql" then options[:database].replace "jdbcmysql" - when "oracle" then options[:database].replace "jdbc" when "sqlite3" then options[:database].replace "jdbcsqlite3" end end @@ -306,7 +304,7 @@ module Rails return [] if options[:skip_sprockets] gems = [] - gems << GemfileEntry.github("sass-rails", "rails/sass-rails", nil, + gems << GemfileEntry.version("sass-rails", "~> 5.0", "Use SCSS for stylesheets") if !options[:skip_javascript] @@ -341,11 +339,6 @@ module Rails gems = [javascript_runtime_gemfile_entry] gems << coffee_gemfile_entry unless options[:skip_coffee] - if options[:javascript] - gems << GemfileEntry.version("#{options[:javascript]}-rails", nil, - "Use #{options[:javascript]} as the JavaScript library") - end - unless options[:skip_turbolinks] gems << GemfileEntry.version("turbolinks", "~> 5", "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") @@ -412,6 +405,10 @@ module Rails !options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin") end + def depends_on_system_test? + !(options[:skip_system_test] || options[:skip_test] || options[:api]) + end + def depend_on_listen? !options[:skip_listen] && os_supports_listen_out_of_the_box? end diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb index d5e326d6ee..97d9ab29d4 100644 --- a/railties/lib/rails/generators/erb.rb +++ b/railties/lib/rails/generators/erb.rb @@ -17,8 +17,8 @@ module Erb # :nodoc: :erb end - def filename_with_extensions(name, format = self.format) - [name, format, handler].compact.join(".") + def filename_with_extensions(name, file_format = format) + [name, file_format, handler].compact.join(".") end end end diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb index 519b6c8603..4f2e84f924 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb @@ -1,4 +1,4 @@ -<%%= form_for(<%= singular_table_name %>) do |f| %> +<%%= form_with(model: <%= singular_table_name %>, local: true) do |form| %> <%% if <%= singular_table_name %>.errors.any? %> <div id="error_explanation"> <h2><%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2> @@ -14,21 +14,21 @@ <% attributes.each do |attribute| -%> <div class="field"> <% if attribute.password_digest? -%> - <%%= f.label :password %> - <%%= f.password_field :password %> + <%%= form.label :password %> + <%%= form.password_field :password, id: :<%= field_id(:password) %> %> </div> <div class="field"> - <%%= f.label :password_confirmation %> - <%%= f.password_field :password_confirmation %> + <%%= form.label :password_confirmation %> + <%%= form.password_field :password_confirmation, id: :<%= field_id(:password_confirmation) %> %> <% else -%> - <%%= f.label :<%= attribute.column_name %> %> - <%%= f.<%= attribute.field_type %> :<%= attribute.column_name %> %> + <%%= form.label :<%= attribute.column_name %> %> + <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, id: :<%= field_id(attribute.column_name) %> %> <% end -%> </div> <% end -%> <div class="actions"> - <%%= f.submit %> + <%%= form.submit %> </div> <%% end %> diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 02557b098a..aef66adb64 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -149,6 +149,10 @@ module Rails "new_#{singular_table_name}_url" end + def field_id(attribute_name) + [singular_table_name, attribute_name].join("_") + end + def singular_table_name # :doc: @singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name) end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 442258c9d1..f4717bb35b 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -84,6 +84,16 @@ module Rails chmod "bin", 0755 & ~File.umask, verbose: false end + def bin_when_updating + bin_yarn_exist = File.exist?("bin/yarn") + + bin + + if options[:api] && !bin_yarn_exist + remove_file "bin/yarn" + end + end + def config empty_directory "config" @@ -106,11 +116,11 @@ module Rails cookie_serializer_config_exist = File.exist?("config/initializers/cookies_serializer.rb") action_cable_config_exist = File.exist?("config/cable.yml") rack_cors_config_exist = File.exist?("config/initializers/cors.rb") + assets_config_exist = File.exist?("config/initializers/assets.rb") + new_framework_defaults_5_1_exist = File.exist?("config/initializers/new_framework_defaults_5_1.rb") config - gsub_file "config/environments/development.rb", /^(\s+)config\.file_watcher/, '\1# config.file_watcher' - unless cookie_serializer_config_exist gsub_file "config/initializers/cookies_serializer.rb", /json(?!,)/, "marshal" end @@ -122,6 +132,22 @@ module Rails unless rack_cors_config_exist remove_file "config/initializers/cors.rb" end + + if options[:api] + unless cookie_serializer_config_exist + remove_file "config/initializers/cookies_serializer.rb" + end + + unless assets_config_exist + remove_file "config/initializers/assets.rb" + end + + # Sprockets owns the only new default for 5.1: + # In API-only Applications, we don't want the file. + unless new_framework_defaults_5_1_exist + remove_file "config/initializers/new_framework_defaults_5_1.rb" + end + end end def database_yml @@ -232,6 +258,11 @@ module Rails build(:bin) end + def update_bin_files + build(:bin_when_updating) + end + remove_task :update_bin_files + def create_config_files build(:config) end @@ -277,7 +308,7 @@ module Rails end def create_system_test_files - build(:system_test) unless options[:skip_system_test] || options[:skip_test] || options[:api] + build(:system_test) if depends_on_system_test? end def create_tmp_files @@ -371,6 +402,12 @@ module Rails end end + def delete_new_framework_defaults + unless options[:update] + remove_file "config/initializers/new_framework_defaults_5_1.rb" + end + end + def delete_bin_yarn_if_skip_yarn_option remove_file "bin/yarn" if options[:skip_yarn] end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index b082d70cba..06f0dd6d6d 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -32,9 +32,9 @@ end group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] - <%- unless options.skip_system_test? || options.api? -%> + <%- if depends_on_system_test? -%> # Adds support for Capybara system testing and selenium driver - gem 'capybara', '~> 2.7.0' + gem 'capybara', '~> 2.13.0' gem 'selenium-webdriver' <%- end -%> end diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt index 25870f19c8..4206002a1b 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt @@ -1,7 +1,7 @@ // This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's // vendor/assets/javascripts directory can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the @@ -11,9 +11,6 @@ // about supported directives. // <% unless options[:skip_javascript] -%> -<% if options[:javascript] -%> -//= require <%= options[:javascript] %> -<% end -%> //= require rails-ujs <% unless options[:skip_turbolinks] -%> //= require turbolinks diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css index 865300bef9..d05ea0f511 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css @@ -2,7 +2,7 @@ * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's * vendor/assets/stylesheets directory can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the bottom of the diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt index c6607dbb2b..52b3de5ee5 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt @@ -19,9 +19,9 @@ chdir APP_ROOT do <% unless options[:skip_yarn] %> # Install JavaScript dependencies if using Yarn # system('bin/yarn') - <% end %> <% unless options.skip_active_record -%> + # puts "\n== Copying sample files ==" # unless File.exist?('config/database.yml') # cp 'config/database.yml.sample', 'config/database.yml' diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt index d23af018c7..d385b363c6 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt @@ -17,6 +17,7 @@ chdir APP_ROOT do system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') <% unless options.skip_active_record -%> + puts "\n== Updating database ==" system! 'bin/rails db:migrate' <% end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn index 4ae896a8d3..44f75c22a4 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/yarn +++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn @@ -3,7 +3,8 @@ Dir.chdir(VENDOR_PATH) do begin exec "yarnpkg #{ARGV.join(" ")}" rescue Errno::ENOENT - puts "Yarn executable was not detected in the system." - puts "Download Yarn at https://yarnpkg.com/en/docs/install" + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index c0a0bd0a3e..0b1d22228e 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -22,6 +22,9 @@ Bundler.require(*Rails.groups) module <%= app_const_base %> class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults <%= Rails::VERSION::STRING.to_f %> + # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. @@ -31,6 +34,10 @@ module <%= app_const_base %> # Middleware like session, flash, cookies can be added back manually. # Skip views, helpers and assets when generating a new resource. config.api_only = true +<%- elsif !depends_on_system_test? -%> + + # Don't generate system test files. + config.generators.system_tests = nil <%- end -%> end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml index d2499ea4fb..6da0601b24 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml @@ -1,4 +1,4 @@ -# Oracle/OCI 8i, 9, 10g +# Oracle/OCI 11g or higher recommended # # Requires Ruby/OCI8: # https://github.com/kubo/ruby-oci8 @@ -17,7 +17,7 @@ # cursor_sharing: similar # default: &default - adapter: oracle + adapter: oracle_enhanced pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <%= app_name %> password: @@ -45,7 +45,9 @@ test: # On Heroku and other platform providers, you may have a full connection URL # available as an environment variable. For example: # -# DATABASE_URL="oracle://myuser:mypass@localhost/somedatabase" +# DATABASE_URL="oracle-enhanced://myuser:mypass@localhost/somedatabase" +# +# Note that the adapter name uses a dash instead of an underscore. # # You can use this database configuration with: # diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 511b4a82eb..b75b65c8df 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -13,6 +13,7 @@ Rails.application.configure do config.consider_all_requests_local = true # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt deleted file mode 100644 index bd844f0503..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt +++ /dev/null @@ -1,37 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.0 upgrade. -# -<%- if options[:update] -%> -# Once upgraded flip defaults one by one to migrate to the new default. -# -<%- end -%> -# Read the Guide for Upgrading Ruby on Rails for more info on each option. -<%- unless options[:api] -%> - -# Enable per-form CSRF tokens. Previous versions had false. -Rails.application.config.action_controller.per_form_csrf_tokens = <%= options[:update] ? false : true %> - -# Enable origin-checking CSRF mitigation. Previous versions had false. -Rails.application.config.action_controller.forgery_protection_origin_check = <%= options[:update] ? false : true %> -<%- end -%> - -# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. -# Previous versions had false. -ActiveSupport.to_time_preserves_timezone = <%= options[:update] ? false : true %> -<%- unless options[:skip_active_record] -%> - -# Require `belongs_to` associations by default. Previous versions had false. -Rails.application.config.active_record.belongs_to_required_by_default = <%= options[:update] ? false : true %> -<%- end -%> -<%- unless options[:update] -%> - -# Configure SSL options to enable HSTS with subdomains. Previous versions had false. -Rails.application.config.ssl_options = { hsts: { subdomains: true } } -<%- end -%> -<%- unless options[:skip_sprockets] -%> - -# Unknown asset fallback will return the path passed in when the given -# asset is not present in the asset pipeline. -Rails.application.config.assets.unknown_asset_fallback = <%= options[:update] ? true : false %> -<%- end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_1.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_1.rb.tt new file mode 100644 index 0000000000..a0c7f44b60 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_1.rb.tt @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.1 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Make `form_with` generate non-remote forms. +Rails.application.config.action_view.form_with_generates_remote_forms = false +<%- unless options[:skip_sprockets] -%> + +# Unknown asset fallback will return the path passed in when the given +# asset is not present in the asset pipeline. +# Rails.application.config.assets.unknown_asset_fallback = false +<%- end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml index 816efcc5b1..ea9d47396c 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml @@ -12,8 +12,8 @@ # Shared secrets are available across all environments. -shared: - api_key: 123 +# shared: +# api_key: a1B2c3D4e5F6 # Environmental secrets are only available for that specific environment. diff --git a/railties/lib/rails/generators/rails/app/templates/public/404.html b/railties/lib/rails/generators/rails/app/templates/public/404.html index b612547fc2..2be3af26fc 100644 --- a/railties/lib/rails/generators/rails/app/templates/public/404.html +++ b/railties/lib/rails/generators/rails/app/templates/public/404.html @@ -4,7 +4,7 @@ <title>The page you were looking for doesn't exist (404)</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> - body { + .rails-default-error-page { background-color: #EFEFEF; color: #2E2F30; text-align: center; @@ -12,13 +12,13 @@ margin: 0; } - div.dialog { + .rails-default-error-page div.dialog { width: 95%; max-width: 33em; margin: 4em auto 0; } - div.dialog > div { + .rails-default-error-page div.dialog > div { border: 1px solid #CCC; border-right-color: #999; border-left-color: #999; @@ -31,13 +31,13 @@ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); } - h1 { + .rails-default-error-page h1 { font-size: 100%; color: #730E15; line-height: 1.5em; } - div.dialog > p { + .rails-default-error-page div.dialog > p { margin: 0 0 1em; padding: 1em; background-color: #F7F7F7; @@ -54,7 +54,7 @@ </style> </head> -<body> +<body class="rails-default-error-page"> <!-- This file lives in public/404.html --> <div class="dialog"> <div> diff --git a/railties/lib/rails/generators/rails/app/templates/public/422.html b/railties/lib/rails/generators/rails/app/templates/public/422.html index a21f82b3bd..c08eac0d1d 100644 --- a/railties/lib/rails/generators/rails/app/templates/public/422.html +++ b/railties/lib/rails/generators/rails/app/templates/public/422.html @@ -4,7 +4,7 @@ <title>The change you wanted was rejected (422)</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> - body { + .rails-default-error-page { background-color: #EFEFEF; color: #2E2F30; text-align: center; @@ -12,13 +12,13 @@ margin: 0; } - div.dialog { + .rails-default-error-page div.dialog { width: 95%; max-width: 33em; margin: 4em auto 0; } - div.dialog > div { + .rails-default-error-page div.dialog > div { border: 1px solid #CCC; border-right-color: #999; border-left-color: #999; @@ -31,13 +31,13 @@ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); } - h1 { + .rails-default-error-page h1 { font-size: 100%; color: #730E15; line-height: 1.5em; } - div.dialog > p { + .rails-default-error-page div.dialog > p { margin: 0 0 1em; padding: 1em; background-color: #F7F7F7; @@ -54,7 +54,7 @@ </style> </head> -<body> +<body class="rails-default-error-page"> <!-- This file lives in public/422.html --> <div class="dialog"> <div> diff --git a/railties/lib/rails/generators/rails/app/templates/public/500.html b/railties/lib/rails/generators/rails/app/templates/public/500.html index 061abc587d..78a030af22 100644 --- a/railties/lib/rails/generators/rails/app/templates/public/500.html +++ b/railties/lib/rails/generators/rails/app/templates/public/500.html @@ -4,7 +4,7 @@ <title>We're sorry, but something went wrong (500)</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> - body { + .rails-default-error-page { background-color: #EFEFEF; color: #2E2F30; text-align: center; @@ -12,13 +12,13 @@ margin: 0; } - div.dialog { + .rails-default-error-page div.dialog { width: 95%; max-width: 33em; margin: 4em auto 0; } - div.dialog > div { + .rails-default-error-page div.dialog > div { border: 1px solid #CCC; border-right-color: #999; border-left-color: #999; @@ -31,13 +31,13 @@ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); } - h1 { + .rails-default-error-page h1 { font-size: 100%; color: #730E15; line-height: 1.5em; } - div.dialog > p { + .rails-default-error-page div.dialog > p { margin: 0 0 1em; padding: 1em; background-color: #F7F7F7; @@ -54,7 +54,7 @@ </style> </head> -<body> +<body class="rails-default-error-page"> <!-- This file lives in public/500.html --> <div class="dialog"> <div> diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index ca48919f9a..118e44d9d0 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -301,7 +301,7 @@ task default: :test end def engine? - full? || mountable? + full? || mountable? || options[:engine] end def full? diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb index af3be10a31..6bdb673215 100644 --- a/railties/lib/rails/paths.rb +++ b/railties/lib/rails/paths.rb @@ -205,7 +205,14 @@ module Rails # Returns all expanded paths but only if they exist in the filesystem. def existent - expanded.select { |f| File.exist?(f) } + expanded.select do |f| + does_exist = File.exist?(f) + + if !does_exist && File.symlink?(f) + raise "File #{f.inspect} is a symlink that does not point to a valid file" + end + does_exist + end end def existent_directories diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index 474118c0a9..3476bb0eb5 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -162,10 +162,6 @@ module Rails @instance ||= new end - def respond_to_missing?(*args) - instance.respond_to?(*args) || super - end - # Allows you to configure the railtie. This is the same method seen in # Railtie::Configurable, but this module is no longer required for all # subclasses of Railtie so we provide the class method here. @@ -178,6 +174,10 @@ module Rails ActiveSupport::Inflector.underscore(string).tr("/", "_") end + def respond_to_missing?(name, _) + instance.respond_to?(name) || super + end + # If the class method does not have a method, then send the method call # to the Railtie instance. def method_missing(name, *args, &block) diff --git a/railties/lib/rails/secrets.rb b/railties/lib/rails/secrets.rb index a083914109..8b644f212c 100644 --- a/railties/lib/rails/secrets.rb +++ b/railties/lib/rails/secrets.rb @@ -1,4 +1,5 @@ require "yaml" +require "active_support/message_encryptor" module Rails # Greatly inspired by Ara T. Howard's magnificent sekrets gem. 😘 @@ -12,12 +13,11 @@ module Rails end end - @read_encrypted_secrets = false + @cipher = "aes-128-gcm" @root = File # Wonky, but ensures `join` uses the current directory. class << self - attr_writer :root - attr_accessor :read_encrypted_secrets + attr_writer :root def parse(paths, env:) paths.each_with_object(Hash.new) do |path, all_secrets| @@ -30,20 +30,19 @@ module Rails end def generate_key - cipher = new_cipher - SecureRandom.hex(cipher.key_len)[0, cipher.key_len] + SecureRandom.hex(OpenSSL::Cipher.new(@cipher).key_len) end def key ENV["RAILS_MASTER_KEY"] || read_key_file || handle_missing_key end - def encrypt(text) - cipher(:encrypt, text) + def encrypt(data) + encryptor.encrypt_and_sign(data) end def decrypt(data) - cipher(:decrypt, data) + encryptor.decrypt_and_verify(data) end def read @@ -87,24 +86,14 @@ module Rails def preprocess(path) if path.end_with?(".enc") - if @read_encrypted_secrets - decrypt(IO.binread(path)) - else - "" - end + decrypt(IO.binread(path)) else IO.read(path) end end - def new_cipher - OpenSSL::Cipher.new("aes-256-cbc") - end - - def cipher(mode, data) - cipher = new_cipher.public_send(mode) - cipher.key = key - cipher.update(data) << cipher.final + def encryptor + @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: @cipher) end end end diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake index c92b42f6c1..177b138090 100644 --- a/railties/lib/rails/tasks/engine.rake +++ b/railties/lib/rails/tasks/engine.rake @@ -1,6 +1,17 @@ task "load_app" do namespace :app do load APP_RAKEFILE + + desc "Update some initially generated files" + task update: [ "update:bin" ] + + namespace :update do + require "rails/engine/updater" + # desc "Adds new executables to the engine bin/ directory" + task :bin do + Rails::Engine::Updater.run(:create_bin_files) + end + end end task environment: "app:environment" diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index f5586b53f0..32a6b109bc 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -63,7 +63,7 @@ namespace :app do # desc "Adds new executables to the application bin/ directory" task :bin do - RailsUpdate.invoke_from_app_generator :create_bin_files + RailsUpdate.invoke_from_app_generator :update_bin_files end task :upgrade_guide_info do diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index 8e290239bd..0f9bf98737 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -11,10 +11,6 @@ require "rails/generators/test_case" require "active_support/testing/autorun" -if defined?(Capybara) && defined?(Puma) - require "action_dispatch/system_test_case" -end - if defined?(ActiveRecord::Base) ActiveRecord::Migration.maintain_test_schema! @@ -48,12 +44,3 @@ class ActionDispatch::IntegrationTest super end end - -if defined?(Capybara) && defined?(Puma) - class ActionDispatch::SystemTestCase - def before_setup # :nodoc: - @routes = Rails.application.routes - super - end - end -end diff --git a/railties/lib/rails/test_unit/minitest_plugin.rb b/railties/lib/rails/test_unit/minitest_plugin.rb index e44fe78bbd..8decdb0f4f 100644 --- a/railties/lib/rails/test_unit/minitest_plugin.rb +++ b/railties/lib/rails/test_unit/minitest_plugin.rb @@ -62,9 +62,9 @@ module Minitest options[:patterns] = opts.order! unless run_via.rake? end - def self.rake_run(patterns) # :nodoc: + def self.rake_run(patterns, exclude_patterns = []) # :nodoc: self.run_via = :rake unless run_via.set? - ::Rails::TestRequirer.require_files(patterns) + ::Rails::TestRequirer.require_files(patterns, exclude_patterns) autorun end @@ -88,7 +88,13 @@ module Minitest # If run via `ruby` we've been passed the files to run directly, or if run # via `rake` then they have already been eagerly required. unless run_via.ruby? || run_via.rake? - ::Rails::TestRequirer.require_files(options[:patterns]) + # If there are no given patterns, we can assume that the user + # simply runs the `bin/rails test` command without extra arguments. + if options[:patterns].empty? + ::Rails::TestRequirer.require_files(options[:patterns], ["test/system/**/*"]) + else + ::Rails::TestRequirer.require_files(options[:patterns]) + end end unless options[:full_backtrace] || ENV["BACKTRACE"] diff --git a/railties/lib/rails/test_unit/test_requirer.rb b/railties/lib/rails/test_unit/test_requirer.rb index fe35934abc..92e5fcf0bc 100644 --- a/railties/lib/rails/test_unit/test_requirer.rb +++ b/railties/lib/rails/test_unit/test_requirer.rb @@ -4,10 +4,13 @@ require "rake/file_list" module Rails class TestRequirer # :nodoc: class << self - def require_files(patterns) + def require_files(patterns, exclude_patterns = []) patterns = expand_patterns(patterns) - Rake::FileList[patterns.compact.presence || "test/**/*_test.rb"].to_a.each do |file| + file_list = Rake::FileList[patterns.compact.presence || "test/**/*_test.rb"] + file_list.exclude(exclude_patterns) + + file_list.to_a.each do |file| require File.expand_path(file) end end diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index 4dde3d3c97..ef19bd7626 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -4,15 +4,15 @@ require "rails/test_unit/minitest_plugin" task default: :test -desc "Runs all tests in test folder" +desc "Runs all tests in test folder except system ones" task :test do $: << "test" - pattern = if ENV.key?("TEST") - ENV["TEST"] + + if ENV.key?("TEST") + Minitest.rake_run([ENV["TEST"]]) else - "test" + Minitest.rake_run(["test"], ["test/system/**/*"]) end - Minitest.rake_run([pattern]) end namespace :test do diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 14433fbba0..07c6afed7f 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -693,6 +693,66 @@ module ApplicationTests assert_match(/label/, last_response.body) end + test "form_with can be configured with form_with_generates_remote_forms" do + app_file "config/initializers/form_builder.rb", <<-RUBY + Rails.configuration.action_view.form_with_generates_remote_forms = false + RUBY + + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + assert_no_match(/data-remote/, last_response.body) + end + + test "form_with generates remote forms by default" do + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + assert_match(/data-remote/, last_response.body) + end + test "default method for update can be changed" do app_file "app/models/post.rb", <<-RUBY class Post diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index ee0d697599..fe581db286 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -189,6 +189,9 @@ module ApplicationTests FileUtils.cd(rails_root) do output = `bin/rails generate --help` assert_no_match "active_record:migration", output + + output = `bin/rails destroy --help` + assert_no_match "active_record:migration", output end end end diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 90927159dd..eb2c578f91 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -262,5 +262,13 @@ module ApplicationTests Rails.env = orig_rails_env if orig_rails_env end end + + test "connections checked out during initialization are returned to the pool" do + app_file "config/initializers/active_record.rb", <<-RUBY + ActiveRecord::Base.connection + RUBY + require "#{app_path}/config/environment" + assert !ActiveRecord::Base.connection_pool.active_connection? + end end end diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb index dc1d816dc5..93b5263bf7 100644 --- a/railties/test/application/middleware/cache_test.rb +++ b/railties/test/application/middleware/cache_test.rb @@ -117,12 +117,12 @@ module ApplicationTests assert_equal "miss, store", last_response.headers["X-Rack-Cache"] assert_equal "public", last_response.headers["Cache-Control"] - body = last_response.body etag = last_response.headers["ETag"] - get "/expires/expires_etag", {}, "If-None-Match" => etag + get "/expires/expires_etag", {}, "HTTP_IF_NONE_MATCH" => etag assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"] - assert_equal body, last_response.body + assert_equal 304, last_response.status + assert_equal "", last_response.body end def test_cache_works_with_etags_private @@ -137,7 +137,7 @@ module ApplicationTests body = last_response.body etag = last_response.headers["ETag"] - get "/expires/expires_etag", { private: true }, "If-None-Match" => etag + get "/expires/expires_etag", { private: true }, "HTTP_IF_NONE_MATCH" => etag assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end @@ -151,12 +151,12 @@ module ApplicationTests assert_equal "miss, store", last_response.headers["X-Rack-Cache"] assert_equal "public", last_response.headers["Cache-Control"] - body = last_response.body last = last_response.headers["Last-Modified"] - get "/expires/expires_last_modified", {}, "If-Modified-Since" => last + get "/expires/expires_last_modified", {}, "HTTP_IF_MODIFIED_SINCE" => last assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"] - assert_equal body, last_response.body + assert_equal 304, last_response.status + assert_equal "", last_response.body end def test_cache_works_with_last_modified_private @@ -171,7 +171,7 @@ module ApplicationTests body = last_response.body last = last_response.headers["Last-Modified"] - get "/expires/expires_last_modified", { private: true }, "If-Modified-Since" => last + get "/expires/expires_last_modified", { private: true }, "HTTP_IF_MODIFIED_SINCE" => last assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index 76cb302c62..2c9770e147 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -37,6 +37,22 @@ module ApplicationTests end end + test "migration with empty version" do + Dir.chdir(app_path) do + output = `bin/rails db:migrate VERSION= 2>&1` + assert_match(/Empty VERSION provided/, output) + + output = `bin/rails db:migrate:redo VERSION= 2>&1` + assert_match(/Empty VERSION provided/, output) + + output = `bin/rails db:migrate:up VERSION= 2>&1` + assert_match(/VERSION is required/, output) + + output = `bin/rails db:migrate:down VERSION= 2>&1` + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + end + end + test "model and migration generator with change syntax" do Dir.chdir(app_path) do `bin/rails generate model user username:string password:string; @@ -61,7 +77,7 @@ module ApplicationTests assert_equal "Schema migrations table does not exist yet.\n", output end - test "test migration status" do + test "migration status" do Dir.chdir(app_path) do `bin/rails generate model user username:string password:string; bin/rails generate migration add_email_to_users email:string; @@ -101,7 +117,7 @@ module ApplicationTests end end - test "test migration status after rollback and redo" do + test "migration status after rollback and redo" do Dir.chdir(app_path) do `bin/rails generate model user username:string password:string; bin/rails generate migration add_email_to_users email:string; @@ -126,6 +142,62 @@ module ApplicationTests end end + test "migration status after rollback and forward" do + Dir.chdir(app_path) do + `bin/rails generate model user username:string password:string; + bin/rails generate migration add_email_to_users email:string; + bin/rails db:migrate` + + output = `bin/rails db:migrate:status` + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + + `bin/rails db:rollback STEP=2` + output = `bin/rails db:migrate:status` + + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) + + `bin/rails db:forward STEP=2` + output = `bin/rails db:migrate:status` + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + end + end + + test "raise error on any move when current migration does not exist" do + Dir.chdir(app_path) do + `bin/rails generate model user username:string password:string; + bin/rails generate migration add_email_to_users email:string; + bin/rails db:migrate + rm db/migrate/*email*.rb` + + output = `bin/rails db:migrate:status` + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) + + output = `bin/rails db:rollback 2>&1` + assert_match(/rails aborted!/, output) + assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output) + assert_match(/No migration with version number\s\d{14}\./, output) + + output = `bin/rails db:migrate:status` + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) + + output = `bin/rails db:forward 2>&1` + assert_match(/rails aborted!/, output) + assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output) + assert_match(/No migration with version number\s\d{14}\./, output) + + output = `bin/rails db:migrate:status` + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) + end + end + test "migration status after rollback and redo without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") @@ -208,7 +280,7 @@ module ApplicationTests end end - test "test migration status migrated file is deleted" do + test "migration status migrated file is deleted" do Dir.chdir(app_path) do `bin/rails generate model user username:string password:string; bin/rails generate migration add_email_to_users email:string; @@ -216,7 +288,6 @@ module ApplicationTests rm db/migrate/*email*.rb` output = `bin/rails db:migrate:status` - File.write("test.txt", output) assert_match(/up\s+\d{14}\s+Create users/, output) assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb index 7d058f6ee6..0c45bc398a 100644 --- a/railties/test/application/runner_test.rb +++ b/railties/test/application/runner_test.rb @@ -35,6 +35,14 @@ module ApplicationTests assert_match "42", Dir.chdir(app_path) { `bin/rails runner "puts User.count"` } end + def test_should_set_argv_when_running_code + output = Dir.chdir(app_path) { + # Both long and short args, at start and end of ARGV + `bin/rails runner "puts ARGV.join(',')" --foo a1 -b a2 a3 --moo` + } + assert_equal "--foo,a1,-b,a2,a3,--moo", output.chomp + end + def test_should_run_file app_file "bin/count_users.rb", <<-SCRIPT puts User.count diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index e773b52dbb..23b259b503 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -323,7 +323,7 @@ module ApplicationTests assert true end - test "test line filter does not run this" do + test "line filter does not run this" do assert true end end @@ -576,6 +576,80 @@ module ApplicationTests capture(:stderr) { run_test_command("test/models/warnings_test.rb -w") }) end + def test_reset_sessions_before_rollback_on_system_tests + app_file "test/system/reset_session_before_rollback_test.rb", <<-RUBY + require "application_system_test_case" + + class ResetSessionBeforeRollbackTest < ApplicationSystemTestCase + def teardown_fixtures + puts "rollback" + super + end + + Capybara.singleton_class.prepend(Module.new do + def reset_sessions! + puts "reset sessions" + super + end + end) + + test "dummy" do + end + end + RUBY + + run_test_command("test/system/reset_session_before_rollback_test.rb").tap do |output| + assert_match "reset sessions\nrollback", output + assert_match "1 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output + end + end + + def test_system_tests_are_not_run_with_the_default_test_command + app_file "test/system/dummy_test.rb", <<-RUBY + require "application_system_test_case" + + class DummyTest < ApplicationSystemTestCase + test "something" do + assert true + end + end + RUBY + + run_test_command("").tap do |output| + assert_match "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output + end + end + + def test_system_tests_are_not_run_through_rake_test + app_file "test/system/dummy_test.rb", <<-RUBY + require "application_system_test_case" + + class DummyTest < ApplicationSystemTestCase + test "something" do + assert true + end + end + RUBY + + output = Dir.chdir(app_path) { `bin/rake test` } + assert_match "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output + end + + def test_system_tests_are_run_through_rake_test_when_given_in_TEST + app_file "test/system/dummy_test.rb", <<-RUBY + require "application_system_test_case" + + class DummyTest < ApplicationSystemTestCase + test "something" do + assert true + end + end + RUBY + + output = Dir.chdir(app_path) { `bin/rake test TEST=test/system/dummy_test.rb` } + assert_match "1 runs, 1 assertions, 0 failures, 0 errors, 0 skips", output + end + private def run_test_command(arguments = "test/unit/test_test.rb") Dir.chdir(app_path) { `bin/rails t #{arguments}` } diff --git a/railties/test/command/base_test.rb b/railties/test/command/base_test.rb new file mode 100644 index 0000000000..ebfc4d0ba9 --- /dev/null +++ b/railties/test/command/base_test.rb @@ -0,0 +1,11 @@ +require "abstract_unit" +require "rails/command" +require "rails/commands/generate/generate_command" +require "rails/commands/secrets/secrets_command" + +class Rails::Command::BaseTest < ActiveSupport::TestCase + test "printing commands" do + assert_equal %w(generate), Rails::Command::GenerateCommand.printing_commands + assert_equal %w(secrets:setup secrets:edit), Rails::Command::SecretsCommand.printing_commands + end +end diff --git a/railties/test/commands/secrets_test.rb b/railties/test/commands/secrets_test.rb new file mode 100644 index 0000000000..00b0343397 --- /dev/null +++ b/railties/test/commands/secrets_test.rb @@ -0,0 +1,37 @@ +require "isolation/abstract_unit" +require "rails/command" +require "rails/commands/secrets/secrets_command" + +class Rails::Command::SecretsCommandTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + end + + def teardown + teardown_app + end + + test "edit without editor gives hint" do + assert_match "No $EDITOR to open decrypted secrets in", run_edit_command(editor: "") + end + + test "edit secrets" do + run_setup_command + + # Run twice to ensure encrypted secrets can be reread after first edit pass. + 2.times do + assert_match(/external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289…/, run_edit_command) + end + end + + private + def run_edit_command(editor: "cat") + Dir.chdir(app_path) { `EDITOR="#{editor}" bin/rails secrets:edit` } + end + + def run_setup_command + Dir.chdir(app_path) { `bin/rails secrets:setup` } + end +end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index d21a80982b..2d1f071969 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -139,6 +139,14 @@ class Rails::ServerTest < ActiveSupport::TestCase end end + def test_argument_precedence_over_environment_variable + switch_env "HOST", "1.2.3.4" do + args = ["-b", "127.0.0.1"] + options = parse_arguments(args) + assert_equal "127.0.0.1", options[:Host] + end + end + def test_records_user_supplied_options server_options = parse_arguments(["-p", 3001]) assert_equal [:Port], server_options[:user_supplied_options] diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb index bdef1798f8..2edb39c8e8 100644 --- a/railties/test/generators/api_app_generator_test.rb +++ b/railties/test/generators/api_app_generator_test.rb @@ -61,13 +61,26 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_skips_per_form_csrf_token_and_origin_check_configs_for_api_apps + def test_app_update_does_not_generate_unnecessary_config_files run_generator - assert_file "config/initializers/new_framework_defaults.rb" do |initializer_content| - assert_no_match(/per_form_csrf_tokens/, initializer_content) - assert_no_match(/forgery_protection_origin_check/, initializer_content) - end + generator = Rails::Generators::AppGenerator.new ["rails"], + { api: true, update: true }, destination_root: destination_root, shell: @shell + quietly { generator.send(:update_config_files) } + + assert_no_file "config/initializers/cookies_serializer.rb" + assert_no_file "config/initializers/assets.rb" + assert_no_file "config/initializers/new_framework_defaults_5_1.rb" + end + + def test_app_update_does_not_generate_unnecessary_bin_files + run_generator + + generator = Rails::Generators::AppGenerator.new ["rails"], + { api: true, update: true }, destination_root: destination_root, shell: @shell + quietly { generator.send(:update_bin_files) } + + assert_no_file "bin/yarn" end private diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 986afb6d2a..8a51beb380 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -133,7 +133,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_rails_update_generates_correct_session_key + def test_app_update_generates_correct_session_key app_root = File.join(destination_root, "myapp") run_generator [app_root] @@ -156,66 +156,65 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_file "config/initializers/cors.rb" end - def test_rails_update_keep_the_cookie_serializer_if_it_is_already_configured - app_root = File.join(destination_root, "myapp") + def test_new_application_doesnt_need_defaults + assert_no_file "config/initializers/new_framework_defaults_5_1.rb" + end + + def test_new_application_load_defaults + app_root = File.join(destination_root, "myfirstapp") run_generator [app_root] + output = nil - stub_rails_application(app_root) do - generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell - generator.send(:app_const) - quietly { generator.send(:update_config_files) } - assert_file("#{app_root}/config/initializers/cookies_serializer.rb", /Rails\.application\.config\.action_dispatch\.cookies_serializer = :json/) + Dir.chdir(app_root) do + output = `./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"` end + + assert_equal "false\n", output end - def test_rails_update_set_the_cookie_serializer_to_marshal_if_it_is_not_already_configured + def test_app_update_keep_the_cookie_serializer_if_it_is_already_configured app_root = File.join(destination_root, "myapp") run_generator [app_root] - FileUtils.rm("#{app_root}/config/initializers/cookies_serializer.rb") - stub_rails_application(app_root) do generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell generator.send(:app_const) quietly { generator.send(:update_config_files) } - assert_file("#{app_root}/config/initializers/cookies_serializer.rb", - /Valid options are :json, :marshal, and :hybrid\.\nRails\.application\.config\.action_dispatch\.cookies_serializer = :marshal/) + assert_file("#{app_root}/config/initializers/cookies_serializer.rb", /Rails\.application\.config\.action_dispatch\.cookies_serializer = :json/) end end - def test_rails_update_dont_set_file_watcher + def test_app_update_set_the_cookie_serializer_to_marshal_if_it_is_not_already_configured app_root = File.join(destination_root, "myapp") run_generator [app_root] + FileUtils.rm("#{app_root}/config/initializers/cookies_serializer.rb") + stub_rails_application(app_root) do generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell generator.send(:app_const) quietly { generator.send(:update_config_files) } - assert_file "#{app_root}/config/environments/development.rb" do |content| - assert_match(/# config.file_watcher/, content) - end + assert_file("#{app_root}/config/initializers/cookies_serializer.rb", + /Valid options are :json, :marshal, and :hybrid\.\nRails\.application\.config\.action_dispatch\.cookies_serializer = :marshal/) end end - def test_rails_update_does_not_create_new_framework_defaults_by_default + def test_app_update_create_new_framework_defaults app_root = File.join(destination_root, "myapp") run_generator [app_root] - FileUtils.rm("#{app_root}/config/initializers/new_framework_defaults.rb") + assert_no_file "#{app_root}/config/initializers/new_framework_defaults_5_1.rb" stub_rails_application(app_root) do generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, destination_root: app_root, shell: @shell generator.send(:app_const) quietly { generator.send(:update_config_files) } - assert_file "#{app_root}/config/initializers/new_framework_defaults.rb" do |content| - assert_match(/Rails\.application\.config.active_record\.belongs_to_required_by_default = false/, content) - assert_no_match(/Rails\.application\.config\.ssl_options/, content) - end + assert_file "#{app_root}/config/initializers/new_framework_defaults_5_1.rb" end end - def test_rails_update_does_not_create_rack_cors + def test_app_update_does_not_create_rack_cors app_root = File.join(destination_root, "myapp") run_generator [app_root] @@ -227,7 +226,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_rails_update_does_not_remove_rack_cors_if_already_present + def test_app_update_does_not_remove_rack_cors_if_already_present app_root = File.join(destination_root, "myapp") run_generator [app_root] @@ -367,10 +366,6 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "bin/update" do |update_content| assert_no_match(/db:migrate/, update_content) end - - assert_file "config/initializers/new_framework_defaults.rb" do |initializer_content| - assert_no_match(/belongs_to_required_by_default/, initializer_content) - end end def test_generator_if_skip_action_mailer_is_given @@ -415,9 +410,6 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_match(/config\.assets\.js_compressor = :uglifier/, content) assert_no_match(/config\.assets\.css_compressor = :sass/, content) end - assert_file "config/initializers/new_framework_defaults.rb" do |content| - assert_no_match(/unknown_asset_fallback/, content) - end end def test_generator_if_skip_yarn_is_given @@ -443,6 +435,41 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "Gemfile", /^# gem 'redis'/ end + def test_generator_if_skip_test_is_given + run_generator [destination_root, "--skip-test"] + assert_file "Gemfile" do |content| + assert_no_match(/capybara/, content) + assert_no_match(/selenium-webdriver/, content) + end + end + + def test_generator_if_skip_system_test_is_given + run_generator [destination_root, "--skip_system_test"] + assert_file "Gemfile" do |content| + assert_no_match(/capybara/, content) + assert_no_match(/selenium-webdriver/, content) + end + end + + def test_does_not_generate_system_test_files_if_skip_system_test_is_given + run_generator [destination_root, "--skip_system_test"] + + Dir.chdir(destination_root) do + quietly { `./bin/rails g scaffold User` } + + assert_no_file("test/application_system_test_case.rb") + assert_no_file("test/system/users_test.rb") + end + end + + def test_generator_if_api_is_given + run_generator [destination_root, "--api"] + assert_file "Gemfile" do |content| + assert_no_match(/capybara/, content) + assert_no_match(/selenium-webdriver/, content) + end + end + def test_inclusion_of_javascript_runtime run_generator if defined?(JRUBY_VERSION) @@ -459,14 +486,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_inclusion_of_javascript_libraries_if_required - run_generator [destination_root, "-j", "jquery"] - assert_file "app/assets/javascripts/application.js" do |contents| - assert_match %r{^//= require jquery}, contents - end - assert_gem "jquery-rails" - end - def test_javascript_is_skipped_if_required run_generator [destination_root, "--skip-javascript"] diff --git a/railties/test/generators/encrypted_secrets_generator_test.rb b/railties/test/generators/encrypted_secrets_generator_test.rb index 747abf19ed..21fdcab19f 100644 --- a/railties/test/generators/encrypted_secrets_generator_test.rb +++ b/railties/test/generators/encrypted_secrets_generator_test.rb @@ -12,11 +12,11 @@ class EncryptedSecretsGeneratorTest < Rails::Generators::TestCase def test_generates_key_file_and_encrypted_secrets_file run_generator - assert_file "config/secrets.yml.key", /[\w\d]+/ + assert_file "config/secrets.yml.key", /\w+/ assert File.exist?("config/secrets.yml.enc") - assert_no_match(/production:\n# external_api_key: [\w\d]+/, IO.binread("config/secrets.yml.enc")) - assert_match(/production:\n# external_api_key: [\w\d]+/, Rails::Secrets.read) + assert_no_match(/production:\n# external_api_key: \w+/, IO.binread("config/secrets.yml.enc")) + assert_match(/production:\n# external_api_key: \w+/, Rails::Secrets.read) end def test_appends_to_gitignore diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 8ec096e5c6..afb37b6a99 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -1,6 +1,7 @@ require "generators/generators_test_helper" require "rails/generators/rails/plugin/plugin_generator" require "generators/shared_generator_tests" +require "rails/engine/updater" DEFAULT_PLUGIN_FILES = %w( .gitignore @@ -731,6 +732,21 @@ class PluginGeneratorTest < Rails::Generators::TestCase end end + def test_app_update_generates_bin_file + run_generator [destination_root, "--mountable"] + + Object.const_set("ENGINE_ROOT", destination_root) + FileUtils.rm("#{destination_root}/bin/rails") + + quietly { Rails::Engine::Updater.run(:create_bin_files) } + + assert_file "#{destination_root}/bin/rails" do |content| + assert_match(%r|APP_PATH = File\.expand_path\('\.\./\.\./test/dummy/config/application', __FILE__\)|, content) + end + ensure + Object.send(:remove_const, "ENGINE_ROOT") + end + private def action(*args, &block) diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 436fbd5d73..bc76cead18 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -437,8 +437,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "app/views/accounts/_form.html.erb" do |content| - assert_match(/^\W{4}<%= f\.text_field :name %>/, content) - assert_match(/^\W{4}<%= f\.text_field :currency_id %>/, content) + assert_match(/^\W{4}<%= form\.text_field :name, id: :account_name %>/, content) + assert_match(/^\W{4}<%= form\.text_field :currency_id, id: :account_currency_id %>/, content) end end @@ -461,8 +461,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "app/views/users/_form.html.erb" do |content| - assert_match(/<%= f\.password_field :password %>/, content) - assert_match(/<%= f\.password_field :password_confirmation %>/, content) + assert_match(/<%= form\.password_field :password, id: :user_password %>/, content) + assert_match(/<%= form\.password_field :password_confirmation, id: :user_password_confirmation %>/, content) end assert_file "app/views/users/index.html.erb" do |content| @@ -558,4 +558,59 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_match(/6 runs, 8 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end + + def test_scaffold_on_invoke_inside_mountable_engine + Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --mountable` } + engine_path = File.join(destination_root, "bukkits") + + Dir.chdir(engine_path) do + quietly { `bin/rails generate scaffold User name:string age:integer` } + + assert File.exist?("app/models/bukkits/user.rb") + assert File.exist?("test/models/bukkits/user_test.rb") + assert File.exist?("test/fixtures/bukkits/users.yml") + + assert File.exist?("app/controllers/bukkits/users_controller.rb") + assert File.exist?("test/controllers/bukkits/users_controller_test.rb") + + assert File.exist?("app/views/bukkits/users/index.html.erb") + assert File.exist?("app/views/bukkits/users/edit.html.erb") + assert File.exist?("app/views/bukkits/users/show.html.erb") + assert File.exist?("app/views/bukkits/users/new.html.erb") + assert File.exist?("app/views/bukkits/users/_form.html.erb") + + assert File.exist?("app/helpers/bukkits/users_helper.rb") + + assert File.exist?("app/assets/javascripts/bukkits/users.js") + assert File.exist?("app/assets/stylesheets/bukkits/users.css") + end + end + + def test_scaffold_on_revoke_inside_mountable_engine + Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --mountable` } + engine_path = File.join(destination_root, "bukkits") + + Dir.chdir(engine_path) do + quietly { `bin/rails generate scaffold User name:string age:integer` } + quietly { `bin/rails destroy scaffold User` } + + assert_not File.exist?("app/models/bukkits/user.rb") + assert_not File.exist?("test/models/bukkits/user_test.rb") + assert_not File.exist?("test/fixtures/bukkits/users.yml") + + assert_not File.exist?("app/controllers/bukkits/users_controller.rb") + assert_not File.exist?("test/controllers/bukkits/users_controller_test.rb") + + assert_not File.exist?("app/views/bukkits/users/index.html.erb") + assert_not File.exist?("app/views/bukkits/users/edit.html.erb") + assert_not File.exist?("app/views/bukkits/users/show.html.erb") + assert_not File.exist?("app/views/bukkits/users/new.html.erb") + assert_not File.exist?("app/views/bukkits/users/_form.html.erb") + + assert_not File.exist?("app/helpers/bukkits/users_helper.rb") + + assert_not File.exist?("app/assets/javascripts/bukkits/users.js") + assert_not File.exist?("app/assets/stylesheets/bukkits/users.css") + end + end end diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 68ba435393..c3c16b6f86 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -200,7 +200,7 @@ class GeneratorsTest < Rails::Generators::TestCase self.class.class_eval(<<-end_eval, __FILE__, __LINE__ + 1) class WithOptionsGenerator < Rails::Generators::Base - class_option :generate, :default => true + class_option :generate, default: true, type: :boolean end end_eval diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 924503a522..2e742a005e 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -119,7 +119,7 @@ module TestHelpers end routes = File.read("#{app_path}/config/routes.rb") - if routes =~ /(\n\s*end\s*)\Z/ + if routes =~ /(\n\s*end\s*)\z/ File.open("#{app_path}/config/routes.rb", "w") do |f| f.puts $` + "\nActiveSupport::Deprecation.silence { match ':controller(/:action(/:id))(.:format)', via: :all }\n" + $1 end @@ -251,7 +251,7 @@ module TestHelpers def add_to_config(str) environment = File.read("#{app_path}/config/application.rb") - if environment =~ /(\n\s*end\s*end\s*)\Z/ + if environment =~ /(\n\s*end\s*end\s*)\z/ File.open("#{app_path}/config/application.rb", "w") do |f| f.puts $` + "\n#{str}\n" + $1 end @@ -260,7 +260,7 @@ module TestHelpers def add_to_env_config(env, str) environment = File.read("#{app_path}/config/environments/#{env}.rb") - if environment =~ /(\n\s*end\s*)\Z/ + if environment =~ /(\n\s*end\s*)\z/ File.open("#{app_path}/config/environments/#{env}.rb", "w") do |f| f.puts $` + "\n#{str}\n" + $1 end diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb index 7b2551062a..f3db0a51d2 100644 --- a/railties/test/paths_test.rb +++ b/railties/test/paths_test.rb @@ -274,3 +274,23 @@ class PathsTest < ActiveSupport::TestCase end end end + +class PathsIntegrationTest < ActiveSupport::TestCase + test "A failed symlink is still a valid file" do + Dir.mktmpdir do |dir| + Dir.chdir(dir) do + FileUtils.mkdir_p("foo") + File.symlink("foo/doesnotexist.rb", "foo/bar.rb") + assert_equal true, File.symlink?("foo/bar.rb") + + root = Rails::Paths::Root.new("foo") + root.add "bar.rb" + + exception = assert_raises(RuntimeError) do + root["bar.rb"].existent + end + assert_match File.expand_path("foo/bar.rb"), exception.message + end + end + end +end diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb index 5838d0d7e7..6639e55382 100644 --- a/railties/test/railties/mounted_engine_test.rb +++ b/railties/test/railties/mounted_engine_test.rb @@ -141,7 +141,7 @@ module ApplicationTests end def engine_asset_path - render inline: "<%= asset_path 'images/foo.png' %>" + render inline: "<%= asset_path 'images/foo.png', skip_pipeline: true %>" end end end diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb index 36e42cf1f9..36c8ef1fd9 100644 --- a/railties/test/secrets_test.rb +++ b/railties/test/secrets_test.rb @@ -9,22 +9,22 @@ class Rails::SecretsTest < ActiveSupport::TestCase def setup build_app - - @old_read_encrypted_secrets, Rails::Secrets.read_encrypted_secrets = - Rails::Secrets.read_encrypted_secrets, true end def teardown - Rails::Secrets.read_encrypted_secrets = @old_read_encrypted_secrets - teardown_app end test "setting read to false skips parsing" do - Rails::Secrets.read_encrypted_secrets = false + run_secrets_generator do + Rails::Secrets.write(<<-end_of_secrets) + test: + yeah_yeah: lets-walk-in-the-cool-evening-light + end_of_secrets - Dir.chdir(app_path) do - assert_equal Hash.new, Rails::Secrets.parse(%w( config/secrets.yml.enc ), env: "production") + Rails.application.config.read_encrypted_secrets = false + Rails.application.instance_variable_set(:@secrets, nil) # Dance around caching 💃🕺 + assert_not Rails.application.secrets.yeah_yeah end end @@ -54,9 +54,9 @@ class Rails::SecretsTest < ActiveSupport::TestCase test "reading from key file" do run_secrets_generator do - File.binwrite("config/secrets.yml.key", "How do I know you feel it?") + File.binwrite("config/secrets.yml.key", "00112233445566778899aabbccddeeff") - assert_equal "How do I know you feel it?", Rails::Secrets.key + assert_equal "00112233445566778899aabbccddeeff", Rails::Secrets.key end end @@ -90,11 +90,27 @@ class Rails::SecretsTest < ActiveSupport::TestCase end_of_secrets Rails.application.config.root = app_path + Rails.application.config.read_encrypted_secrets = true Rails.application.instance_variable_set(:@secrets, nil) # Dance around caching 💃🕺 assert_equal "lets-walk-in-the-cool-evening-light", Rails.application.secrets.yeah_yeah end end + test "refer secrets inside env config" do + run_secrets_generator do + Rails::Secrets.write(<<-end_of_yaml) + production: + some_secret: yeah yeah + end_of_yaml + + add_to_env_config "production", <<-end_of_config + config.dereferenced_secret = Rails.application.secrets.some_secret + end_of_config + + assert_equal "yeah yeah\n", `bin/rails runner -e production "puts Rails.application.config.dereferenced_secret"` + end + end + private def run_secrets_generator Dir.chdir(app_path) do |