aboutsummaryrefslogtreecommitdiffstats
path: root/railties
diff options
context:
space:
mode:
Diffstat (limited to 'railties')
-rw-r--r--railties/.gitignore1
-rw-r--r--railties/CHANGELOG.md427
-rw-r--r--railties/MIT-LICENSE20
-rw-r--r--railties/RDOC_MAIN.rdoc73
-rw-r--r--railties/README.rdoc41
-rw-r--r--railties/Rakefile32
-rwxr-xr-xrailties/exe/rails9
-rw-r--r--railties/lib/rails.rb111
-rw-r--r--railties/lib/rails/all.rb16
-rw-r--r--railties/lib/rails/api/task.rb156
-rw-r--r--railties/lib/rails/app_loader.rb64
-rw-r--r--railties/lib/rails/application.rb525
-rw-r--r--railties/lib/rails/application/bootstrap.rb81
-rw-r--r--railties/lib/rails/application/configuration.rb211
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb117
-rw-r--r--railties/lib/rails/application/finisher.rb122
-rw-r--r--railties/lib/rails/application/routes_reloader.rb54
-rw-r--r--railties/lib/rails/application_controller.rb16
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb32
-rw-r--r--railties/lib/rails/cli.rb15
-rw-r--r--railties/lib/rails/code_statistics.rb100
-rw-r--r--railties/lib/rails/code_statistics_calculator.rb86
-rw-r--r--railties/lib/rails/command.rb70
-rw-r--r--railties/lib/rails/commands.rb19
-rw-r--r--railties/lib/rails/commands/application.rb17
-rw-r--r--railties/lib/rails/commands/commands_tasks.rb180
-rw-r--r--railties/lib/rails/commands/console.rb68
-rw-r--r--railties/lib/rails/commands/console_helper.rb34
-rw-r--r--railties/lib/rails/commands/dbconsole.rb173
-rw-r--r--railties/lib/rails/commands/destroy.rb11
-rw-r--r--railties/lib/rails/commands/dev_cache.rb21
-rw-r--r--railties/lib/rails/commands/generate.rb13
-rw-r--r--railties/lib/rails/commands/plugin.rb23
-rw-r--r--railties/lib/rails/commands/rake_proxy.rb34
-rw-r--r--railties/lib/rails/commands/runner.rb62
-rw-r--r--railties/lib/rails/commands/server.rb143
-rw-r--r--railties/lib/rails/commands/test.rb9
-rw-r--r--railties/lib/rails/configuration.rb135
-rw-r--r--railties/lib/rails/console/app.rb37
-rw-r--r--railties/lib/rails/console/helpers.rb17
-rw-r--r--railties/lib/rails/engine.rb704
-rw-r--r--railties/lib/rails/engine/commands.rb43
-rw-r--r--railties/lib/rails/engine/configuration.rb85
-rw-r--r--railties/lib/rails/engine/railties.rb21
-rw-r--r--railties/lib/rails/gem_version.rb15
-rw-r--r--railties/lib/rails/generators.rb394
-rw-r--r--railties/lib/rails/generators/actions.rb295
-rw-r--r--railties/lib/rails/generators/actions/create_migration.rb69
-rw-r--r--railties/lib/rails/generators/active_model.rb78
-rw-r--r--railties/lib/rails/generators/app_base.rb382
-rw-r--r--railties/lib/rails/generators/base.rb379
-rw-r--r--railties/lib/rails/generators/css/assets/assets_generator.rb13
-rw-r--r--railties/lib/rails/generators/css/assets/templates/stylesheet.css4
-rw-r--r--railties/lib/rails/generators/css/scaffold/scaffold_generator.rb16
-rw-r--r--railties/lib/rails/generators/erb.rb25
-rw-r--r--railties/lib/rails/generators/erb/controller/controller_generator.rb22
-rw-r--r--railties/lib/rails/generators/erb/controller/templates/view.html.erb2
-rw-r--r--railties/lib/rails/generators/erb/mailer/mailer_generator.rb40
-rw-r--r--railties/lib/rails/generators/erb/mailer/templates/layout.html.erb5
-rw-r--r--railties/lib/rails/generators/erb/mailer/templates/layout.text.erb1
-rw-r--r--railties/lib/rails/generators/erb/mailer/templates/view.html.erb5
-rw-r--r--railties/lib/rails/generators/erb/mailer/templates/view.text.erb3
-rw-r--r--railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb31
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb34
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb6
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb31
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/new.html.erb5
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/show.html.erb11
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb174
-rw-r--r--railties/lib/rails/generators/js/assets/assets_generator.rb13
-rw-r--r--railties/lib/rails/generators/js/assets/templates/javascript.js2
-rw-r--r--railties/lib/rails/generators/migration.rb69
-rw-r--r--railties/lib/rails/generators/model_helpers.rb28
-rw-r--r--railties/lib/rails/generators/named_base.rb213
-rw-r--r--railties/lib/rails/generators/rails/app/USAGE15
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb462
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile55
-rw-r--r--railties/lib/rails/generators/rails/app/templates/README.md24
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Rakefile6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt20
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css15
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt24
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/bundle2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/rails3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/rake3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup33
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/update28
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config.ru4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb43
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/boot.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml49
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml85
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml68
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml52
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml68
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml23
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml58
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml58
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml85
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml25
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml68
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environment.rb5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt62
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt84
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt46
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/active_record_belongs_to_required_by_default.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt11
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/callback_terminator.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb16
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb16
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/request_forgery_protection.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt16
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/locales/en.yml23
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/secrets.yml22
-rw-r--r--railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/gitignore22
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/404.html67
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/422.html67
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/500.html66
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/favicon.ico0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/robots.txt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/test_helper.rb12
-rw-r--r--railties/lib/rails/generators/rails/assets/USAGE20
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb25
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/javascript.js2
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/stylesheet.css4
-rw-r--r--railties/lib/rails/generators/rails/controller/USAGE18
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb60
-rw-r--r--railties/lib/rails/generators/rails/controller/templates/controller.rb13
-rw-r--r--railties/lib/rails/generators/rails/generator/USAGE13
-rw-r--r--railties/lib/rails/generators/rails/generator/generator_generator.rb27
-rw-r--r--railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/generator/templates/USAGE.tt8
-rw-r--r--railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/helper/USAGE13
-rw-r--r--railties/lib/rails/generators/rails/helper/helper_generator.rb13
-rw-r--r--railties/lib/rails/generators/rails/helper/templates/helper.rb4
-rw-r--r--railties/lib/rails/generators/rails/integration_test/USAGE10
-rw-r--r--railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb7
-rw-r--r--railties/lib/rails/generators/rails/migration/USAGE35
-rw-r--r--railties/lib/rails/generators/rails/migration/migration_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/model/USAGE114
-rw-r--r--railties/lib/rails/generators/rails/model/model_generator.rb12
-rw-r--r--railties/lib/rails/generators/rails/plugin/USAGE10
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb446
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec24
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/Gemfile47
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE20
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/README.rdoc3
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/Rakefile29
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt14
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt11
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/bin/test.tt8
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/config/routes.rb6
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/gitignore9
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb5
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb7
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb1
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake4
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/application.rb18
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb5
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js11
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js6
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js13
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb3
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css15
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb7
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb8
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb27
-rw-r--r--railties/lib/rails/generators/rails/resource/USAGE23
-rw-r--r--railties/lib/rails/generators/rails/resource/resource_generator.rb19
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb51
-rw-r--r--railties/lib/rails/generators/rails/scaffold/USAGE41
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb33
-rw-r--r--railties/lib/rails/generators/rails/scaffold/templates/scaffold.css84
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/USAGE19
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb31
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb61
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb68
-rw-r--r--railties/lib/rails/generators/rails/task/USAGE9
-rw-r--r--railties/lib/rails/generators/rails/task/task_generator.rb12
-rw-r--r--railties/lib/rails/generators/rails/task/templates/task.rb8
-rw-r--r--railties/lib/rails/generators/resource_helpers.rb82
-rw-r--r--railties/lib/rails/generators/test_case.rb36
-rw-r--r--railties/lib/rails/generators/test_unit.rb8
-rw-r--r--railties/lib/rails/generators/test_unit/controller/controller_generator.rb15
-rw-r--r--railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb25
-rw-r--r--railties/lib/rails/generators/test_unit/generator/generator_generator.rb26
-rw-r--r--railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb16
-rw-r--r--railties/lib/rails/generators/test_unit/helper/helper_generator.rb9
-rw-r--r--railties/lib/rails/generators/test_unit/integration/integration_generator.rb13
-rw-r--r--railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb7
-rw-r--r--railties/lib/rails/generators/test_unit/job/job_generator.rb13
-rw-r--r--railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb9
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb26
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb21
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/templates/preview.rb13
-rw-r--r--railties/lib/rails/generators/test_unit/model/model_generator.rb36
-rw-r--r--railties/lib/rails/generators/test_unit/model/templates/fixtures.yml29
-rw-r--r--railties/lib/rails/generators/test_unit/model/templates/unit_test.rb9
-rw-r--r--railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb13
-rw-r--r--railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt7
-rw-r--r--railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb46
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb43
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb53
-rw-r--r--railties/lib/rails/generators/testing/assertions.rb121
-rw-r--r--railties/lib/rails/generators/testing/behaviour.rb110
-rw-r--r--railties/lib/rails/generators/testing/setup_and_teardown.rb18
-rw-r--r--railties/lib/rails/info.rb102
-rw-r--r--railties/lib/rails/info_controller.rb44
-rw-r--r--railties/lib/rails/initializable.rb89
-rw-r--r--railties/lib/rails/mailers_controller.rb79
-rw-r--r--railties/lib/rails/paths.rb219
-rw-r--r--railties/lib/rails/rack.rb5
-rw-r--r--railties/lib/rails/rack/debugger.rb3
-rw-r--r--railties/lib/rails/rack/logger.rb90
-rw-r--r--railties/lib/rails/railtie.rb250
-rw-r--r--railties/lib/rails/railtie/configurable.rb35
-rw-r--r--railties/lib/rails/railtie/configuration.rb100
-rw-r--r--railties/lib/rails/ruby_version_check.rb13
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb133
-rw-r--r--railties/lib/rails/tasks.rb18
-rw-r--r--railties/lib/rails/tasks/annotations.rake20
-rw-r--r--railties/lib/rails/tasks/engine.rake72
-rw-r--r--railties/lib/rails/tasks/framework.rake68
-rw-r--r--railties/lib/rails/tasks/initializers.rake6
-rw-r--r--railties/lib/rails/tasks/log.rake23
-rw-r--r--railties/lib/rails/tasks/middleware.rake7
-rw-r--r--railties/lib/rails/tasks/misc.rake60
-rw-r--r--railties/lib/rails/tasks/restart.rake5
-rw-r--r--railties/lib/rails/tasks/routes.rake7
-rw-r--r--railties/lib/rails/tasks/statistics.rake28
-rw-r--r--railties/lib/rails/tasks/tmp.rake37
-rw-r--r--railties/lib/rails/templates/layouts/application.html.erb36
-rw-r--r--railties/lib/rails/templates/rails/info/properties.html.erb1
-rw-r--r--railties/lib/rails/templates/rails/info/routes.html.erb9
-rw-r--r--railties/lib/rails/templates/rails/mailers/email.html.erb130
-rw-r--r--railties/lib/rails/templates/rails/mailers/index.html.erb8
-rw-r--r--railties/lib/rails/templates/rails/mailers/mailer.html.erb6
-rw-r--r--railties/lib/rails/templates/rails/welcome/index.html.erb268
-rw-r--r--railties/lib/rails/test_help.rb42
-rw-r--r--railties/lib/rails/test_unit/minitest_plugin.rb91
-rw-r--r--railties/lib/rails/test_unit/railtie.rb18
-rw-r--r--railties/lib/rails/test_unit/reporter.rb74
-rw-r--r--railties/lib/rails/test_unit/test_requirer.rb28
-rw-r--r--railties/lib/rails/test_unit/testing.rake45
-rw-r--r--railties/lib/rails/version.rb8
-rw-r--r--railties/lib/rails/welcome_controller.rb8
-rw-r--r--railties/railties.gemspec34
-rw-r--r--railties/test/abstract_unit.rb33
-rw-r--r--railties/test/app_loader_test.rb89
-rw-r--r--railties/test/application/asset_debugging_test.rb73
-rw-r--r--railties/test/application/assets_test.rb504
-rw-r--r--railties/test/application/bin_setup_test.rb54
-rw-r--r--railties/test/application/configuration/custom_test.rb54
-rw-r--r--railties/test/application/configuration_test.rb1437
-rw-r--r--railties/test/application/console_test.rb165
-rw-r--r--railties/test/application/generators_test.rb164
-rw-r--r--railties/test/application/initializers/frameworks_test.rb269
-rw-r--r--railties/test/application/initializers/hooks_test.rb90
-rw-r--r--railties/test/application/initializers/i18n_test.rb289
-rw-r--r--railties/test/application/initializers/load_path_test.rb110
-rw-r--r--railties/test/application/initializers/notifications_test.rb56
-rw-r--r--railties/test/application/loading_test.rb372
-rw-r--r--railties/test/application/mailer_previews_test.rb701
-rw-r--r--railties/test/application/middleware/cache_test.rb180
-rw-r--r--railties/test/application/middleware/cookies_test.rb47
-rw-r--r--railties/test/application/middleware/exceptions_test.rb125
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb78
-rw-r--r--railties/test/application/middleware/sendfile_test.rb74
-rw-r--r--railties/test/application/middleware/session_test.rb342
-rw-r--r--railties/test/application/middleware/static_test.rb68
-rw-r--r--railties/test/application/middleware_test.rb298
-rw-r--r--railties/test/application/multiple_applications_test.rb176
-rw-r--r--railties/test/application/paths_test.rb83
-rw-r--r--railties/test/application/per_request_digest_cache_test.rb63
-rw-r--r--railties/test/application/rack/logger_test.rb56
-rw-r--r--railties/test/application/rackup_test.rb43
-rw-r--r--railties/test/application/rake/dbs_test.rb313
-rw-r--r--railties/test/application/rake/framework_test.rb48
-rw-r--r--railties/test/application/rake/migrations_test.rb228
-rw-r--r--railties/test/application/rake/notes_test.rb157
-rw-r--r--railties/test/application/rake/restart_test.rb39
-rw-r--r--railties/test/application/rake_test.rb314
-rw-r--r--railties/test/application/rendering_test.rb45
-rw-r--r--railties/test/application/routing_test.rb480
-rw-r--r--railties/test/application/runner_test.rb89
-rw-r--r--railties/test/application/test_runner_test.rb455
-rw-r--r--railties/test/application/test_test.rb307
-rw-r--r--railties/test/application/url_generation_test.rb59
-rw-r--r--railties/test/backtrace_cleaner_test.rb24
-rw-r--r--railties/test/code_statistics_calculator_test.rb330
-rw-r--r--railties/test/code_statistics_test.rb20
-rw-r--r--railties/test/commands/console_test.rb154
-rw-r--r--railties/test/commands/dbconsole_test.rb264
-rw-r--r--railties/test/commands/dev_cache_test.rb32
-rw-r--r--railties/test/commands/server_test.rb111
-rw-r--r--railties/test/configuration/middleware_stack_proxy_test.rb61
-rw-r--r--railties/test/engine_test.rb25
-rw-r--r--railties/test/env_helpers.rb30
-rw-r--r--railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml1
-rw-r--r--railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb1
-rw-r--r--railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb1
-rw-r--r--railties/test/fixtures/lib/create_test_dummy_template.rb1
-rw-r--r--railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb8
-rw-r--r--railties/test/fixtures/lib/generators/fixjour_generator.rb2
-rw-r--r--railties/test/fixtures/lib/generators/model_generator.rb1
-rw-r--r--railties/test/fixtures/lib/generators/usage_template/USAGE1
-rw-r--r--railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb5
-rw-r--r--railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb4
-rw-r--r--railties/test/fixtures/lib/template.rb1
-rw-r--r--railties/test/generators/actions_test.rb316
-rw-r--r--railties/test/generators/api_app_generator_test.rb98
-rw-r--r--railties/test/generators/app_generator_test.rb696
-rw-r--r--railties/test/generators/argv_scrubber_test.rb136
-rw-r--r--railties/test/generators/assets_generator_test.rb19
-rw-r--r--railties/test/generators/controller_generator_test.rb103
-rw-r--r--railties/test/generators/create_migration_test.rb134
-rw-r--r--railties/test/generators/generated_attribute_test.rb152
-rw-r--r--railties/test/generators/generator_generator_test.rb71
-rw-r--r--railties/test/generators/generator_test.rb85
-rw-r--r--railties/test/generators/generators_test_helper.rb50
-rw-r--r--railties/test/generators/helper_generator_test.rb39
-rw-r--r--railties/test/generators/integration_test_generator_test.rb12
-rw-r--r--railties/test/generators/job_generator_test.rb29
-rw-r--r--railties/test/generators/mailer_generator_test.rb186
-rw-r--r--railties/test/generators/migration_generator_test.rb321
-rw-r--r--railties/test/generators/model_generator_test.rb476
-rw-r--r--railties/test/generators/named_base_test.rb139
-rw-r--r--railties/test/generators/namespaced_generators_test.rb423
-rw-r--r--railties/test/generators/orm_test.rb38
-rw-r--r--railties/test/generators/plugin_generator_test.rb636
-rw-r--r--railties/test/generators/plugin_test_helper.rb24
-rw-r--r--railties/test/generators/plugin_test_runner_test.rb104
-rw-r--r--railties/test/generators/resource_generator_test.rb88
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb246
-rw-r--r--railties/test/generators/scaffold_generator_test.rb536
-rw-r--r--railties/test/generators/shared_generator_tests.rb159
-rw-r--r--railties/test/generators/task_generator_test.rb24
-rw-r--r--railties/test/generators/test_runner_in_engine_test.rb31
-rw-r--r--railties/test/generators_test.rb246
-rw-r--r--railties/test/initializable_test.rb230
-rw-r--r--railties/test/isolation/abstract_unit.rb339
-rw-r--r--railties/test/path_generation_test.rb83
-rw-r--r--railties/test/paths_test.rb276
-rw-r--r--railties/test/rack_logger_test.rb75
-rw-r--r--railties/test/rails_info_controller_test.rb81
-rw-r--r--railties/test/rails_info_test.rb71
-rw-r--r--railties/test/railties/engine_test.rb1361
-rw-r--r--railties/test/railties/generators_test.rb134
-rw-r--r--railties/test/railties/mounted_engine_test.rb274
-rw-r--r--railties/test/railties/railtie_test.rb208
-rw-r--r--railties/test/test_unit/reporter_test.rb147
-rw-r--r--railties/test/version_test.rb12
371 files changed, 31957 insertions, 0 deletions
diff --git a/railties/.gitignore b/railties/.gitignore
new file mode 100644
index 0000000000..80dd262d2f
--- /dev/null
+++ b/railties/.gitignore
@@ -0,0 +1 @@
+log/
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
new file mode 100644
index 0000000000..b9d2db773a
--- /dev/null
+++ b/railties/CHANGELOG.md
@@ -0,0 +1,427 @@
+* The generated config file for the development environment includes a new
+ config line, commented out, showing how to enable the evented file watcher.
+
+ *Xavier Noria*
+
+* `config.debug_exception_response_format` configures the format used
+ in responses when errors occur in development mode.
+
+ Set `config.debug_exception_response_format` to render an HTML page with
+ debug info (using the value `:default`) or render debug info preserving
+ the response format (using the value `:api`).
+
+ *Jorge Bejar*
+
+* Fix setting exit status code for rake test tasks. The exit status code
+ was not set when tests were fired with `rake`. Now, it is being set and it matches
+ behavior of running tests via `rails` command (`rails test`), so no matter if
+ `rake test` or `rails test` command is used the exit code will be set.
+
+ *Arkadiusz Fal*
+
+* Add Command infrastructure to replace rake.
+
+ Also move `rake dev:cache` to new infrastructure. You'll need to use
+ `rails dev:cache` to toggle development caching from now on.
+
+ *Chuck Callebs*
+
+* Allow use of minitest-rails gem with Rails test runner.
+
+ Fixes #22455.
+
+ *Chris Kottom*
+
+* Add `bin/test` script to rails plugin.
+
+ `bin/test` can use the same API as `bin/rails test`.
+
+ *Yuji Yaginuma*
+
+* Make `static_index` part of the `config.public_file_server` config and
+ call it `public_file_server.index_name`.
+
+ *Yuki Nishijima*
+
+* Deprecate `serve_static_files` in favor of `public_file_server.enabled`.
+
+ Unifies the static asset options under `public_file_server`.
+
+ To upgrade, replace occurrences of:
+
+ ```
+ config.serve_static_files = # false or true
+ ```
+
+ in your environment files, with:
+
+ ```
+ config.public_file_server.enabled = # false or true
+ ```
+
+ *Kasper Timm Hansen*
+
+* Deprecate `config.static_cache_control` in favor of
+ `config.public_file_server.headers`.
+
+ To upgrade, replace occurrences of:
+
+ ```
+ config.static_cache_control = 'public, max-age=60'
+ ```
+
+ in your environment files, with:
+
+ ```
+ config.public_file_server.headers = {
+ 'Cache-Control' => 'public, max-age=60'
+ }
+ ```
+
+ `config.public_file_server.headers` can set arbitrary headers, sent along when
+ a response is delivered.
+
+ *Yuki Nishijima*
+
+* Route generator should be idempotent
+ running generators several times no longer require you to cleanup routes.rb
+
+ *Thiago Pinto*
+
+* Allow passing an environment to `config_for`.
+
+ *Simon Eskildsen*
+
+* Allow rake:stats to account for rake tasks in lib/tasks
+
+ *Kevin Deisz*
+
+* Added javascript to update the URL on mailer previews with the currently
+ selected email format. Reloading the page now keeps you on your selected
+ format rather than going back to the default html version.
+
+ *James Kerr*
+
+* Add fail fast to `bin/rails test`
+
+ Adding `--fail-fast` or `-f` when running tests will interrupt the run on
+ the first failure:
+
+ ```
+ # Running:
+
+ ................................................S......E
+
+ ArgumentError: Wups! Bet you didn't expect this!
+ test/models/bunny_test.rb:19:in `block in <class:BunnyTest>'
+
+ bin/rails test test/models/bunny_test.rb:18
+
+ ....................................F
+
+ This failed
+
+ bin/rails test test/models/bunny_test.rb:14
+
+ Interrupted. Exiting...
+
+
+ Finished in 0.051427s, 1808.3872 runs/s, 1769.4972 assertions/s.
+
+ ```
+
+ Note that any unexpected errors don't abort the run.
+
+ *Kasper Timm Hansen*
+
+* Add inline output to `bin/rails test`
+
+ Any failures or errors (and skips if running in verbose mode) are output
+ during a test run:
+
+ ```
+ # Running:
+
+ .....S..........................................F
+
+ This failed
+
+ bin/rails test test/models/bunny_test.rb:14
+
+ .................................E
+
+ ArgumentError: Wups! Bet you didn't expect this!
+ test/models/bunny_test.rb:19:in `block in <class:BunnyTest>'
+
+ bin/rails test test/models/bunny_test.rb:18
+
+ ....................
+
+ Finished in 0.069708s, 1477.6019 runs/s, 1448.9106 assertions/s.
+ ```
+
+ Output can be deferred to after a run with the `--defer-output` option.
+
+ *Kasper Timm Hansen*
+
+* Fix displaying mailer previews on non local requests when config
+ `action_mailer.show_previews` is set
+
+ *Wojciech Wnętrzak*
+
+* `rails server` will now honour the `PORT` environment variable
+
+ *David Cornu*
+
+* Plugins generated using `rails plugin new` are now generated with the
+ version number set to 0.1.0.
+
+ *Daniel Morris*
+
+* `I18n.load_path` is now reloaded under development so there's no need to
+ restart the server to make new locale files available. Also, I18n will no
+ longer raise for deleted locale files.
+
+ *Kir Shatrov*
+
+* Add `bin/update` script to update development environment automatically.
+
+ *Mehmet Emin İNAÇ*
+
+* Fix STATS_DIRECTORIES already defined warning when running rake from within
+ the top level directory of an engine that has a test app.
+
+ Fixes #20510
+
+ *Ersin Akinci*
+
+* Make enabling or disabling caching in development mode possible with
+ rake dev:cache.
+
+ Running rake dev:cache will create or remove tmp/caching-dev.txt. When this
+ file exists config.action_controller.perform_caching will be set to true in
+ config/environments/development.rb.
+
+ Additionally, a server can be started with either --dev-caching or
+ --no-dev-caching included to toggle caching on startup.
+
+ *Jussi Mertanen*, *Chuck Callebs*
+
+* Add a `--api` option in order to generate plugins that can be added
+ inside an API application.
+
+ *Robin Dupret*
+
+* Fix `NoMethodError` when generating a scaffold inside a full engine.
+
+ *Yuji Yaginuma*
+
+* Adding support for passing a block to the `add_source` action of a custom generator
+
+ *Mike Dalton*, *Hirofumi Wakasugi*
+
+* `assert_file` understands paths with special characters
+ (eg. `v0.1.4~alpha+nightly`).
+
+ *Diego Carrion*
+
+* Remove ContentLength middleware from the defaults. If you want it, just
+ add it as a middleware in your config.
+
+ *Egg McMuffin*
+
+* Make it possible to customize the executable inside rerun snippets.
+
+ *Yves Senn*
+
+* Add support for API only apps.
+ Middleware stack was slimmed down and it has only the needed
+ middleware for API apps & generators generates the right files,
+ folders and configurations.
+
+ *Santiago Pastorino & Jorge Bejar*
+
+* Make generated scaffold functional tests work inside engines.
+
+ *Yuji Yaginuma*
+
+* Generator a `.keep` file in the `tmp` folder by default as many scripts
+ assume the existence of this folder and most would fail if it is absent.
+
+ See #20299.
+
+ *Yoong Kang Lim*, *Sunny Juneja*
+
+* `config.static_index` configures directory `index.html` filename
+
+ Set `config.static_index` to serve a static directory index file not named
+ `index`. E.g. to serve `main.html` instead of `index.html` for directory
+ requests, set `config.static_index` to `"main"`.
+
+ *Eliot Sykes*
+
+* `bin/setup` uses built-in rake tasks (`log:clear`, `tmp:clear`).
+
+ *Mohnish Thallavajhula*
+
+* Fix mailer previews with attachments by using the mail gem's own API to
+ locate the first part of the correct mime type.
+
+ Fixes #14435.
+
+ *Andrew White*
+
+* Remove sqlite support from `rails dbconsole`.
+
+ *Andrew White*
+
+* Rename `railties/bin` to `railties/exe` to match the new Bundler executables
+ convention.
+
+ *Islam Wazery*
+
+* Print `bundle install` output in `rails new` as soon as it's available.
+
+ Running `rails new` will now print the output of `bundle install` as
+ it is available, instead of waiting until all gems finish installing.
+
+ *Max Holder*
+
+* Respect `pluralize_table_names` when generating fixture file.
+
+ Fixes #19519.
+
+ *Yuji Yaginuma*
+
+* Add a new-line to the end of route method generated code.
+
+ We need to add a `\n`, because we cannot have two routes
+ in the same line.
+
+ *arthurnn*
+
+* Add `rake initializers`.
+
+ This task prints out all defined initializers in the order they are invoked
+ by Rails. This is helpful for debugging issues related to the initialization
+ process.
+
+ *Naoto Kaneko*
+
+* Created rake restart task. Restarts your Rails app by touching the
+ `tmp/restart.txt`.
+
+ Fixes #18876.
+
+ *Hyonjee Joo*
+
+* Add `config/initializers/active_record_belongs_to_required_by_default.rb`.
+
+ Newly generated Rails apps have a new initializer called
+ `active_record_belongs_to_required_by_default.rb` which sets the value of
+ the configuration option `config.active_record.belongs_to_required_by_default`
+ to `true` when ActiveRecord is not skipped.
+
+ As a result, new Rails apps require `belongs_to` association on model
+ to be valid.
+
+ This initializer is *not* added when running `rake rails:update`, so
+ old apps ported to Rails 5 will work without any change.
+
+ *Josef Šimánek*
+
+* `delete` operations in configurations are run last in order to eliminate
+ 'No such middleware' errors when `insert_before` or `insert_after` are added
+ after the `delete` operation for the middleware being deleted.
+
+ Fixes #16433.
+
+ *Guo Xiang Tan*
+
+* Newly generated applications get a `README.md` in Markdown.
+
+ *Xavier Noria*
+
+* Remove the documentation tasks `doc:app`, `doc:rails`, and `doc:guides`.
+
+ *Xavier Noria*
+
+* Force generated routes to be inserted into `config/routes.rb`.
+
+ *Andrew White*
+
+* Don't remove all line endings from `config/routes.rb` when revoking scaffold.
+
+ Fixes #15913.
+
+ *Andrew White*
+
+* Rename `--skip-test-unit` option to `--skip-test` in app generator
+
+ *Melanie Gilman*
+
+* Add the `method_source` gem to the default Gemfile for apps.
+
+ *Sean Griffin*
+
+* Drop old test locations from `rake stats`:
+
+ - test/functional
+ - test/unit
+
+ *Ravil Bayramgalin*
+
+* Update `rake stats` to correctly count declarative tests
+ as methods in `_test.rb` files.
+
+ *Ravil Bayramgalin*
+
+* Remove deprecated `test:all` and `test:all:db` tasks.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `Rails::Rack::LogTailer`.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `RAILS_CACHE` constant.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `serve_static_assets` configuration.
+
+ *Rafael Mendonça França*
+
+* Use local variables in `_form.html.erb` partial generated by scaffold.
+
+ *Andrew Kozlov*
+
+* Add `config/initializers/callback_terminator.rb`.
+
+ Newly generated Rails apps have a new initializer called
+ `callback_terminator.rb` which sets the value of the configuration option
+ `ActiveSupport.halt_callback_chains_on_return_false` to `false`.
+
+ As a result, new Rails apps do not halt Active Record and Active Model
+ callback chains when a callback returns `false`; only when they are
+ explicitly halted with `throw(:abort)`.
+
+ The terminator is *not* added when running `rake rails:update`, so returning
+ `false` will still work on old apps ported to Rails 5, displaying a
+ deprecation warning to prompt users to update their code to the new syntax.
+
+ *claudiob*
+
+* Generated fixtures won't use the id when generated with references attributes.
+
+ *Pablo Olmos de Aguilera Corradini*
+
+* Add `--skip-action-mailer` option to the app generator.
+
+ *claudiob*
+
+* Autoload any second level directories called `app/*/concerns`.
+
+ *Alex Robbin*
+
+Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/railties/CHANGELOG.md) for previous changes.
diff --git a/railties/MIT-LICENSE b/railties/MIT-LICENSE
new file mode 100644
index 0000000000..7c2197229d
--- /dev/null
+++ b/railties/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2004-2015 David Heinemeier Hansson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc
new file mode 100644
index 0000000000..8d847eaa1c
--- /dev/null
+++ b/railties/RDOC_MAIN.rdoc
@@ -0,0 +1,73 @@
+== Welcome to \Rails
+
+\Rails is a web-application framework that includes everything needed to create
+database-backed web applications according to the {Model-View-Controller (MVC)}[http://en.wikipedia.org/wiki/Model-view-controller] pattern.
+
+Understanding the MVC pattern is key to understanding \Rails. MVC divides your application
+into three layers, each with a specific responsibility.
+
+The View layer is composed of "templates" that are responsible for providing
+appropriate representations of your application's resources. Templates
+can come in a variety of formats, but most view templates are \HTML with embedded Ruby
+code (.erb files).
+
+The Model layer represents your domain model (such as Account, Product, Person, Post)
+and encapsulates the business logic that is specific to your application. In \Rails,
+database-backed model classes are derived from ActiveRecord::Base. Active Record allows
+you to present the data from database rows as objects and embellish these data objects
+with business logic methods. Although most \Rails models are backed by a database, models
+can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as
+provided by the ActiveModel module. You can read more about Active Record in its
+{README}[link:files/activerecord/README_rdoc.html].
+
+The Controller layer is responsible for handling incoming HTTP requests and providing a
+suitable response. Usually this means returning \HTML, but \Rails controllers can also
+generate XML, JSON, PDFs, mobile-specific views, and more. Controllers manipulate models
+and render view templates in order to generate the appropriate HTTP response.
+
+In \Rails, the Controller and View layers are handled together by Action Pack.
+These two layers are bundled in a single package due to their heavy interdependence.
+This is unlike the relationship between Active Record and Action Pack, which are
+independent. Each of these packages can be used independently outside of \Rails. You
+can read more about Action Pack in its {README}[link:files/actionpack/README_rdoc.html].
+
+== Getting Started
+
+1. Install \Rails at the command prompt if you haven't yet:
+
+ gem install rails
+
+2. At the command prompt, create a new \Rails application:
+
+ rails new myapp
+
+ where "myapp" is the application name.
+
+3. Change directory to +myapp+ and start the web server:
+
+ cd myapp; rails server
+
+ Run with <tt>--help</tt> or <tt>-h</tt> for options.
+
+4. Go to http://localhost:3000 and you'll see:
+
+ "Welcome aboard: You're riding Ruby on Rails!"
+
+5. Follow the guidelines to start developing your application. You may find the following resources handy:
+
+* The \README file created within your application.
+* {Getting Started with \Rails}[http://guides.rubyonrails.org/getting_started.html].
+* {Ruby on \Rails Tutorial}[http://ruby.railstutorial.org/ruby-on-rails-tutorial-book].
+* {Ruby on \Rails Guides}[http://guides.rubyonrails.org].
+* {The API Documentation}[http://api.rubyonrails.org].
+
+== Contributing
+
+We encourage you to contribute to Ruby on \Rails! Please check out the {Contributing to Rails
+guide}[http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how
+to proceed. {Join us}[http://contributors.rubyonrails.org]!
+
+
+== License
+
+Ruby on \Rails is released under the {MIT License}[http://www.opensource.org/licenses/MIT].
diff --git a/railties/README.rdoc b/railties/README.rdoc
new file mode 100644
index 0000000000..a25658668c
--- /dev/null
+++ b/railties/README.rdoc
@@ -0,0 +1,41 @@
+= Railties -- Gluing the Engine to the Rails
+
+Railties is responsible for gluing all frameworks together. Overall, it:
+
+* handles the bootstrapping process for a Rails application;
+
+* manages the +rails+ command line interface;
+
+* and provides the Rails generators core.
+
+
+== Download
+
+The latest version of Railties can be installed with RubyGems:
+
+* gem install railties
+
+Source code can be downloaded as part of the Rails project on GitHub
+
+* https://github.com/rails/rails/tree/master/railties
+
+== License
+
+Railties is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
+
+== Support
+
+API documentation is at
+
+* http://api.rubyonrails.org
+
+Bug reports can be filed for the Ruby on Rails project here:
+
+* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/railties/Rakefile b/railties/Rakefile
new file mode 100644
index 0000000000..cf130a5f14
--- /dev/null
+++ b/railties/Rakefile
@@ -0,0 +1,32 @@
+require 'rake/testtask'
+
+task :default => :test
+
+desc "Run all unit tests"
+task :test => 'test:isolated'
+
+namespace :test do
+ task :isolated do
+ dirs = (ENV["TEST_DIR"] || ENV["TEST_DIRS"] || "**").split(",")
+ test_files = dirs.map { |dir| "test/#{dir}/*_test.rb" }
+ Dir[*test_files].each do |file|
+ next true if file.include?("fixtures")
+ dash_i = [
+ 'test',
+ 'lib',
+ "#{File.dirname(__FILE__)}/../activesupport/lib",
+ "#{File.dirname(__FILE__)}/../actionpack/lib",
+ "#{File.dirname(__FILE__)}/../activemodel/lib"
+ ]
+ ruby "-w", "-I#{dash_i.join ':'}", file
+ end
+ end
+end
+
+Rake::TestTask.new('test:regular') do |t|
+ t.libs << 'test' << "#{File.dirname(__FILE__)}/../activesupport/lib"
+ t.pattern = 'test/**/*_test.rb'
+ t.warning = false
+ t.verbose = true
+ t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
+end
diff --git a/railties/exe/rails b/railties/exe/rails
new file mode 100755
index 0000000000..82c17cabce
--- /dev/null
+++ b/railties/exe/rails
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+
+git_path = File.expand_path('../../../.git', __FILE__)
+
+if File.exist?(git_path)
+ railties_path = File.expand_path('../../lib', __FILE__)
+ $:.unshift(railties_path)
+end
+require "rails/cli"
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
new file mode 100644
index 0000000000..fe789f3c2a
--- /dev/null
+++ b/railties/lib/rails.rb
@@ -0,0 +1,111 @@
+require 'rails/ruby_version_check'
+
+require 'pathname'
+
+require 'active_support'
+require 'active_support/dependencies/autoload'
+require 'active_support/core_ext/kernel/reporting'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/array/extract_options'
+
+require 'rails/application'
+require 'rails/version'
+
+require 'active_support/railtie'
+require 'action_dispatch/railtie'
+
+# UTF-8 is the default internal and external encoding.
+silence_warnings do
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = Encoding::UTF_8
+end
+
+module Rails
+ extend ActiveSupport::Autoload
+
+ autoload :Info
+ autoload :InfoController
+ autoload :MailersController
+ autoload :WelcomeController
+
+ class << self
+ @application = @app_class = nil
+
+ attr_writer :application
+ attr_accessor :app_class, :cache, :logger
+ def application
+ @application ||= (app_class.instance if app_class)
+ end
+
+ delegate :initialize!, :initialized?, to: :application
+
+ # The Configuration instance used to configure the Rails environment
+ def configuration
+ application.config
+ end
+
+ def backtrace_cleaner
+ @backtrace_cleaner ||= begin
+ # Relies on Active Support, so we have to lazy load to postpone definition until AS has been loaded
+ require 'rails/backtrace_cleaner'
+ Rails::BacktraceCleaner.new
+ end
+ end
+
+ # Returns a Pathname object of the current rails project,
+ # otherwise it returns nil if there is no project:
+ #
+ # Rails.root
+ # # => #<Pathname:/Users/someuser/some/path/project>
+ def root
+ application && application.config.root
+ end
+
+ # Returns the current Rails environment.
+ #
+ # Rails.env # => "development"
+ # Rails.env.development? # => true
+ # Rails.env.production? # => false
+ def env
+ @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
+ end
+
+ # Sets the Rails environment.
+ #
+ # Rails.env = "staging" # => "staging"
+ def env=(environment)
+ @_env = ActiveSupport::StringInquirer.new(environment)
+ end
+
+ # Returns all rails groups for loading based on:
+ #
+ # * The Rails environment;
+ # * The environment variable RAILS_GROUPS;
+ # * The optional envs given as argument and the hash with group dependencies;
+ #
+ # groups assets: [:development, :test]
+ #
+ # # Returns
+ # # => [:default, :development, :assets] for Rails.env == "development"
+ # # => [:default, :production] for Rails.env == "production"
+ def groups(*groups)
+ hash = groups.extract_options!
+ env = Rails.env
+ groups.unshift(:default, env)
+ groups.concat ENV["RAILS_GROUPS"].to_s.split(",")
+ groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
+ groups.compact!
+ groups.uniq!
+ groups
+ end
+
+ # Returns a Pathname object of the public folder of the current
+ # rails project, otherwise it returns nil if there is no project:
+ #
+ # Rails.public_path
+ # # => #<Pathname:/Users/someuser/some/path/project/public>
+ def public_path
+ application && Pathname.new(application.paths["public"].first)
+ end
+ end
+end
diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb
new file mode 100644
index 0000000000..45361fca83
--- /dev/null
+++ b/railties/lib/rails/all.rb
@@ -0,0 +1,16 @@
+require "rails"
+
+%w(
+ active_record
+ action_controller
+ action_view
+ action_mailer
+ active_job
+ rails/test_unit
+ sprockets
+).each do |framework|
+ begin
+ require "#{framework}/railtie"
+ rescue LoadError
+ end
+end
diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb
new file mode 100644
index 0000000000..a082932632
--- /dev/null
+++ b/railties/lib/rails/api/task.rb
@@ -0,0 +1,156 @@
+require 'rdoc/task'
+
+module Rails
+ module API
+ class Task < RDoc::Task
+ RDOC_FILES = {
+ 'activesupport' => {
+ :include => %w(
+ README.rdoc
+ lib/active_support/**/*.rb
+ ),
+ :exclude => 'lib/active_support/vendor/*'
+ },
+
+ 'activerecord' => {
+ :include => %w(
+ README.rdoc
+ lib/active_record/**/*.rb
+ )
+ },
+
+ 'activemodel' => {
+ :include => %w(
+ README.rdoc
+ lib/active_model/**/*.rb
+ )
+ },
+
+ 'actionpack' => {
+ :include => %w(
+ README.rdoc
+ lib/abstract_controller/**/*.rb
+ lib/action_controller/**/*.rb
+ lib/action_dispatch/**/*.rb
+ )
+ },
+
+ 'actionview' => {
+ :include => %w(
+ README.rdoc
+ lib/action_view/**/*.rb
+ ),
+ :exclude => 'lib/action_view/vendor/*'
+ },
+
+ 'actionmailer' => {
+ :include => %w(
+ README.rdoc
+ lib/action_mailer/**/*.rb
+ )
+ },
+
+ 'activejob' => {
+ :include => %w(
+ README.md
+ lib/active_job/**/*.rb
+ )
+ },
+
+ 'railties' => {
+ :include => %w(
+ README.rdoc
+ lib/**/*.rb
+ ),
+ :exclude => 'lib/rails/generators/rails/**/templates/**/*.rb'
+ }
+ }
+
+ def initialize(name)
+ super
+
+ # Every time rake runs this task is instantiated as all the rest.
+ # Be lazy computing stuff to have as light impact as possible to
+ # the rest of tasks.
+ before_running_rdoc do
+ load_and_configure_sdoc
+ configure_rdoc_files
+ setup_horo_variables
+ end
+ end
+
+ # Hack, ignore the desc calls performed by the original initializer.
+ def desc(description)
+ # no-op
+ end
+
+ def load_and_configure_sdoc
+ require 'sdoc'
+
+ self.title = 'Ruby on Rails API'
+ self.rdoc_dir = api_dir
+
+ options << '-m' << api_main
+ options << '-e' << 'UTF-8'
+
+ options << '-f' << 'sdoc'
+ options << '-T' << 'rails'
+ rescue LoadError
+ $stderr.puts %(Unable to load SDoc, please add\n\n gem 'sdoc', require: false\n\nto the Gemfile.)
+ exit 1
+ end
+
+ def configure_rdoc_files
+ rdoc_files.include(api_main)
+
+ RDOC_FILES.each do |component, cfg|
+ cdr = component_root_dir(component)
+
+ Array(cfg[:include]).each do |pattern|
+ rdoc_files.include("#{cdr}/#{pattern}")
+ end
+
+ Array(cfg[:exclude]).each do |pattern|
+ rdoc_files.exclude("#{cdr}/#{pattern}")
+ end
+ end
+ end
+
+ def setup_horo_variables
+ ENV['HORO_PROJECT_NAME'] = 'Ruby on Rails'
+ ENV['HORO_PROJECT_VERSION'] = rails_version
+ end
+
+ def api_main
+ component_root_dir('railties') + '/RDOC_MAIN.rdoc'
+ end
+ end
+
+ class RepoTask < Task
+ def load_and_configure_sdoc
+ super
+ options << '-g' # link to GitHub, SDoc flag
+ end
+
+ def component_root_dir(component)
+ component
+ end
+
+ def api_dir
+ 'doc/rdoc'
+ end
+ end
+
+ class EdgeTask < RepoTask
+ def rails_version
+ "master@#{`git rev-parse HEAD`[0, 7]}"
+ end
+ end
+
+ class StableTask < RepoTask
+ def rails_version
+ File.read('RAILS_VERSION').strip
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/app_loader.rb b/railties/lib/rails/app_loader.rb
new file mode 100644
index 0000000000..a9fe21824e
--- /dev/null
+++ b/railties/lib/rails/app_loader.rb
@@ -0,0 +1,64 @@
+require 'pathname'
+require 'rails/version'
+
+module Rails
+ module AppLoader # :nodoc:
+ extend self
+
+ RUBY = Gem.ruby
+ EXECUTABLES = ['bin/rails', 'script/rails']
+ BUNDLER_WARNING = <<EOS
+Looks like your app's ./bin/rails is a stub that was generated by Bundler.
+
+In Rails #{Rails::VERSION::MAJOR}, your app's bin/ directory contains executables that are versioned
+like any other source code, rather than stubs that are generated on demand.
+
+Here's how to upgrade:
+
+ bundle config --delete bin # Turn off Bundler's stub generator
+ rake rails:update:bin # Use the new Rails 4 executables
+ git add bin # Add bin/ to source control
+
+You may need to remove bin/ from your .gitignore as well.
+
+When you install a gem whose executable you want to use in your app,
+generate it and add it to source control:
+
+ bundle binstubs some-gem-name
+ git add bin/new-executable
+
+EOS
+
+ def exec_app
+ original_cwd = Dir.pwd
+
+ loop do
+ if exe = find_executable
+ contents = File.read(exe)
+
+ if contents =~ /(APP|ENGINE)_PATH/
+ exec RUBY, exe, *ARGV
+ break # non reachable, hack to be able to stub exec in the test suite
+ elsif exe.end_with?('bin/rails') && contents.include?('This file was generated by Bundler')
+ $stderr.puts(BUNDLER_WARNING)
+ Object.const_set(:APP_PATH, File.expand_path('config/application', Dir.pwd))
+ require File.expand_path('../boot', APP_PATH)
+ require 'rails/commands'
+ break
+ end
+ end
+
+ # If we exhaust the search there is no executable, this could be a
+ # call to generate a new application, so restore the original cwd.
+ Dir.chdir(original_cwd) and return if Pathname.new(Dir.pwd).root?
+
+ # Otherwise keep moving upwards in search of an executable.
+ Dir.chdir('..')
+ end
+ end
+
+ def find_executable
+ EXECUTABLES.find { |exe| File.file?(exe) }
+ end
+ end
+end
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
new file mode 100644
index 0000000000..77efe3248d
--- /dev/null
+++ b/railties/lib/rails/application.rb
@@ -0,0 +1,525 @@
+require 'fileutils'
+require 'yaml'
+require 'active_support/core_ext/hash/keys'
+require 'active_support/core_ext/object/blank'
+require 'active_support/key_generator'
+require 'active_support/message_verifier'
+require 'rails/engine'
+
+module Rails
+ # An Engine with the responsibility of coordinating the whole boot process.
+ #
+ # == Initialization
+ #
+ # Rails::Application is responsible for executing all railties and engines
+ # initializers. It also executes some bootstrap initializers (check
+ # Rails::Application::Bootstrap) and finishing initializers, after all the others
+ # are executed (check Rails::Application::Finisher).
+ #
+ # == Configuration
+ #
+ # Besides providing the same configuration as Rails::Engine and Rails::Railtie,
+ # the application object has several specific configurations, for example
+ # "cache_classes", "consider_all_requests_local", "filter_parameters",
+ # "logger" and so forth.
+ #
+ # Check Rails::Application::Configuration to see them all.
+ #
+ # == Routes
+ #
+ # The application object is also responsible for holding the routes and reloading routes
+ # whenever the files change in development.
+ #
+ # == Middlewares
+ #
+ # The Application is also responsible for building the middleware stack.
+ #
+ # == Booting process
+ #
+ # The application is also responsible for setting up and executing the booting
+ # process. From the moment you require "config/application.rb" in your app,
+ # the booting process goes like this:
+ #
+ # 1) require "config/boot.rb" to setup load paths
+ # 2) require railties and engines
+ # 3) Define Rails.application as "class MyApp::Application < Rails::Application"
+ # 4) Run config.before_configuration callbacks
+ # 5) Load config/environments/ENV.rb
+ # 6) Run config.before_initialize callbacks
+ # 7) Run Railtie#initializer defined by railties, engines and application.
+ # One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
+ # 8) Custom Railtie#initializers added by railties, engines and applications are executed
+ # 9) Build the middleware stack and run to_prepare callbacks
+ # 10) Run config.before_eager_load and eager_load! if eager_load is true
+ # 11) Run config.after_initialize callbacks
+ #
+ # == Multiple Applications
+ #
+ # If you decide to define multiple applications, then the first application
+ # that is initialized will be set to +Rails.application+, unless you override
+ # it with a different application.
+ #
+ # To create a new application, you can instantiate a new instance of a class
+ # that has already been created:
+ #
+ # class Application < Rails::Application
+ # end
+ #
+ # first_application = Application.new
+ # second_application = Application.new(config: first_application.config)
+ #
+ # In the above example, the configuration from the first application was used
+ # to initialize the second application. You can also use the +initialize_copy+
+ # on one of the applications to create a copy of the application which shares
+ # the configuration.
+ #
+ # If you decide to define rake tasks, runners, or initializers in an
+ # application other than +Rails.application+, then you must run those
+ # these manually.
+ class Application < Engine
+ autoload :Bootstrap, 'rails/application/bootstrap'
+ autoload :Configuration, 'rails/application/configuration'
+ autoload :DefaultMiddlewareStack, 'rails/application/default_middleware_stack'
+ autoload :Finisher, 'rails/application/finisher'
+ autoload :Railties, 'rails/engine/railties'
+ autoload :RoutesReloader, 'rails/application/routes_reloader'
+
+ class << self
+ def inherited(base)
+ super
+ Rails.app_class = base
+ add_lib_to_load_path!(find_root(base.called_from))
+ end
+
+ def instance
+ super.run_load_hooks!
+ end
+
+ def create(initial_variable_values = {}, &block)
+ new(initial_variable_values, &block).run_load_hooks!
+ end
+
+ def find_root(from)
+ find_root_with_flag "config.ru", from, Dir.pwd
+ end
+
+ # Makes the +new+ method public.
+ #
+ # Note that Rails::Application inherits from Rails::Engine, which
+ # inherits from Rails::Railtie and the +new+ method on Rails::Railtie is
+ # private
+ public :new
+ end
+
+ attr_accessor :assets, :sandbox
+ alias_method :sandbox?, :sandbox
+ attr_reader :reloaders
+
+ delegate :default_url_options, :default_url_options=, to: :routes
+
+ INITIAL_VARIABLES = [:config, :railties, :routes_reloader, :reloaders,
+ :routes, :helpers, :app_env_config, :secrets] # :nodoc:
+
+ def initialize(initial_variable_values = {}, &block)
+ super()
+ @initialized = false
+ @reloaders = []
+ @routes_reloader = nil
+ @app_env_config = nil
+ @ordered_railties = nil
+ @railties = nil
+ @message_verifiers = {}
+ @ran_load_hooks = false
+
+ # are these actually used?
+ @initial_variable_values = initial_variable_values
+ @block = block
+ end
+
+ # Returns true if the application is initialized.
+ def initialized?
+ @initialized
+ end
+
+ def run_load_hooks! # :nodoc:
+ return self if @ran_load_hooks
+ @ran_load_hooks = true
+ ActiveSupport.run_load_hooks(:before_configuration, self)
+
+ @initial_variable_values.each do |variable_name, value|
+ if INITIAL_VARIABLES.include?(variable_name)
+ instance_variable_set("@#{variable_name}", value)
+ end
+ end
+
+ instance_eval(&@block) if @block
+ self
+ end
+
+ # Reload application routes regardless if they changed or not.
+ def reload_routes!
+ routes_reloader.reload!
+ end
+
+ # Returns the application's KeyGenerator
+ def key_generator
+ # number of iterations selected based on consultation with the google security
+ # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
+ @caching_key_generator ||=
+ if secrets.secret_key_base
+ unless secrets.secret_key_base.kind_of?(String)
+ raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String, change this value in `config/secrets.yml`"
+ end
+ key_generator = ActiveSupport::KeyGenerator.new(secrets.secret_key_base, iterations: 1000)
+ ActiveSupport::CachingKeyGenerator.new(key_generator)
+ else
+ ActiveSupport::LegacyKeyGenerator.new(secrets.secret_token)
+ end
+ end
+
+ # Returns a message verifier object.
+ #
+ # This verifier can be used to generate and verify signed messages in the application.
+ #
+ # It is recommended not to use the same verifier for different things, so you can get different
+ # verifiers passing the +verifier_name+ argument.
+ #
+ # ==== Parameters
+ #
+ # * +verifier_name+ - the name of the message verifier.
+ #
+ # ==== Examples
+ #
+ # message = Rails.application.message_verifier('sensitive_data').generate('my sensible data')
+ # Rails.application.message_verifier('sensitive_data').verify(message)
+ # # => 'my sensible data'
+ #
+ # See the +ActiveSupport::MessageVerifier+ documentation for more information.
+ def message_verifier(verifier_name)
+ @message_verifiers[verifier_name] ||= begin
+ secret = key_generator.generate_key(verifier_name.to_s)
+ ActiveSupport::MessageVerifier.new(secret)
+ end
+ end
+
+ # Convenience for loading config/foo.yml for the current Rails env.
+ #
+ # Example:
+ #
+ # # config/exception_notification.yml:
+ # production:
+ # url: http://127.0.0.1:8080
+ # namespace: my_app_production
+ # development:
+ # url: http://localhost:3001
+ # namespace: my_app_development
+ #
+ # # config/production.rb
+ # Rails.application.configure do
+ # config.middleware.use ExceptionNotifier, config_for(:exception_notification)
+ # end
+ def config_for(name, env: Rails.env)
+ yaml = Pathname.new("#{paths["config"].existent.first}/#{name}.yml")
+
+ if yaml.exist?
+ require "erb"
+ (YAML.load(ERB.new(yaml.read).result) || {})[env] || {}
+ else
+ raise "Could not load configuration. No such file - #{yaml}"
+ end
+ rescue Psych::SyntaxError => e
+ raise "YAML syntax error occurred while parsing #{yaml}. " \
+ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
+ "Error: #{e.message}"
+ end
+
+ # Stores some of the Rails initial environment parameters which
+ # will be used by middlewares and engines to configure themselves.
+ def env_config
+ @app_env_config ||= begin
+ validate_secret_key_config!
+
+ super.merge({
+ "action_dispatch.parameter_filter" => config.filter_parameters,
+ "action_dispatch.redirect_filter" => config.filter_redirect,
+ "action_dispatch.secret_token" => secrets.secret_token,
+ "action_dispatch.secret_key_base" => secrets.secret_key_base,
+ "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
+ "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
+ "action_dispatch.logger" => Rails.logger,
+ "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
+ "action_dispatch.key_generator" => key_generator,
+ "action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt,
+ "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt,
+ "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt,
+ "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt,
+ "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer,
+ "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest
+ })
+ end
+ end
+
+ # If you try to define a set of rake tasks on the instance, these will get
+ # passed up to the rake tasks defined on the application's class.
+ def rake_tasks(&block)
+ self.class.rake_tasks(&block)
+ end
+
+ # Sends the initializers to the +initializer+ method defined in the
+ # Rails::Initializable module. Each Rails::Application class has its own
+ # set of initializers, as defined by the Initializable module.
+ def initializer(name, opts={}, &block)
+ self.class.initializer(name, opts, &block)
+ end
+
+ # Sends any runner called in the instance of a new application up
+ # to the +runner+ method defined in Rails::Railtie.
+ def runner(&blk)
+ self.class.runner(&blk)
+ end
+
+ # Sends any console called in the instance of a new application up
+ # to the +console+ method defined in Rails::Railtie.
+ def console(&blk)
+ self.class.console(&blk)
+ end
+
+ # Sends any generators called in the instance of a new application up
+ # to the +generators+ method defined in Rails::Railtie.
+ def generators(&blk)
+ self.class.generators(&blk)
+ end
+
+ # Sends the +isolate_namespace+ method up to the class method.
+ def isolate_namespace(mod)
+ self.class.isolate_namespace(mod)
+ end
+
+ ## Rails internal API
+
+ # This method is called just after an application inherits from Rails::Application,
+ # allowing the developer to load classes in lib and use them during application
+ # configuration.
+ #
+ # class MyApplication < Rails::Application
+ # require "my_backend" # in lib/my_backend
+ # config.i18n.backend = MyBackend
+ # end
+ #
+ # Notice this method takes into consideration the default root path. So if you
+ # are changing config.root inside your application definition or having a custom
+ # Rails application, you will need to add lib to $LOAD_PATH on your own in case
+ # you need to load files in lib/ during the application configuration as well.
+ def self.add_lib_to_load_path!(root) #:nodoc:
+ path = File.join root, 'lib'
+ if File.exist?(path) && !$LOAD_PATH.include?(path)
+ $LOAD_PATH.unshift(path)
+ end
+ end
+
+ def require_environment! #:nodoc:
+ environment = paths["config/environment"].existent.first
+ require environment if environment
+ end
+
+ def routes_reloader #:nodoc:
+ @routes_reloader ||= RoutesReloader.new
+ end
+
+ # Returns an array of file paths appended with a hash of
+ # directories-extensions suitable for ActiveSupport::FileUpdateChecker
+ # API.
+ def watchable_args #:nodoc:
+ files, dirs = config.watchable_files.dup, config.watchable_dirs.dup
+
+ ActiveSupport::Dependencies.autoload_paths.each do |path|
+ dirs[path.to_s] = [:rb]
+ end
+
+ [files, dirs]
+ end
+
+ # Initialize the application passing the given group. By default, the
+ # group is :default
+ def initialize!(group=:default) #:nodoc:
+ raise "Application has been already initialized." if @initialized
+ run_initializers(group, self)
+ @initialized = true
+ self
+ end
+
+ def initializers #:nodoc:
+ Bootstrap.initializers_for(self) +
+ railties_initializers(super) +
+ Finisher.initializers_for(self)
+ end
+
+ def config #:nodoc:
+ @config ||= Application::Configuration.new(self.class.find_root(self.class.called_from))
+ end
+
+ def config=(configuration) #:nodoc:
+ @config = configuration
+ end
+
+ # Returns secrets added to config/secrets.yml.
+ #
+ # Example:
+ #
+ # development:
+ # secret_key_base: 836fa3665997a860728bcb9e9a1e704d427cfc920e79d847d79c8a9a907b9e965defa4154b2b86bdec6930adbe33f21364523a6f6ce363865724549fdfc08553
+ # test:
+ # secret_key_base: 5a37811464e7d378488b0f073e2193b093682e4e21f5d6f3ae0a4e1781e61a351fdc878a843424e81c73fb484a40d23f92c8dafac4870e74ede6e5e174423010
+ # production:
+ # secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
+ # namespace: my_app_production
+ #
+ # +Rails.application.secrets.namespace+ returns +my_app_production+ in the
+ # production environment.
+ def secrets
+ @secrets ||= begin
+ secrets = ActiveSupport::OrderedOptions.new
+ yaml = config.paths["config/secrets"].first
+ if File.exist?(yaml)
+ require "erb"
+ all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
+ env_secrets = all_secrets[Rails.env]
+ secrets.merge!(env_secrets.symbolize_keys) if env_secrets
+ end
+
+ # Fallback to config.secret_key_base if secrets.secret_key_base isn't set
+ secrets.secret_key_base ||= config.secret_key_base
+ # Fallback to config.secret_token if secrets.secret_token isn't set
+ secrets.secret_token ||= config.secret_token
+
+ secrets
+ end
+ end
+
+ def secrets=(secrets) #:nodoc:
+ @secrets = secrets
+ end
+
+ def to_app #:nodoc:
+ self
+ end
+
+ def helpers_paths #:nodoc:
+ config.helpers_paths
+ end
+
+ console do
+ require "pp"
+ end
+
+ console do
+ unless ::Kernel.private_method_defined?(:y)
+ require "psych/y"
+ end
+ end
+
+ # Return an array of railties respecting the order they're loaded
+ # and the order specified by the +railties_order+ config.
+ #
+ # While running initializers we need engines in reverse order here when
+ # copying migrations from railties ; we need them in the order given by
+ # +railties_order+.
+ def migration_railties # :nodoc:
+ ordered_railties.flatten - [self]
+ end
+
+ protected
+
+ alias :build_middleware_stack :app
+
+ def run_tasks_blocks(app) #:nodoc:
+ railties.each { |r| r.run_tasks_blocks(app) }
+ super
+ require "rails/tasks"
+ task :environment do
+ ActiveSupport.on_load(:before_initialize) { config.eager_load = false }
+
+ require_environment!
+ end
+ end
+
+ def run_generators_blocks(app) #:nodoc:
+ railties.each { |r| r.run_generators_blocks(app) }
+ super
+ end
+
+ def run_runner_blocks(app) #:nodoc:
+ railties.each { |r| r.run_runner_blocks(app) }
+ super
+ end
+
+ def run_console_blocks(app) #:nodoc:
+ railties.each { |r| r.run_console_blocks(app) }
+ super
+ end
+
+ # Returns the ordered railties for this application considering railties_order.
+ def ordered_railties #:nodoc:
+ @ordered_railties ||= begin
+ order = config.railties_order.map do |railtie|
+ if railtie == :main_app
+ self
+ elsif railtie.respond_to?(:instance)
+ railtie.instance
+ else
+ railtie
+ end
+ end
+
+ all = (railties - order)
+ all.push(self) unless (all + order).include?(self)
+ order.push(:all) unless order.include?(:all)
+
+ index = order.index(:all)
+ order[index] = all
+ order
+ end
+ end
+
+ def railties_initializers(current) #:nodoc:
+ initializers = []
+ ordered_railties.reverse.flatten.each do |r|
+ if r == self
+ initializers += current
+ else
+ initializers += r.initializers
+ end
+ end
+ initializers
+ end
+
+ def default_middleware_stack #:nodoc:
+ default_stack = DefaultMiddlewareStack.new(self, config, paths)
+ default_stack.build_stack
+ end
+
+ def validate_secret_key_config! #:nodoc:
+ if secrets.secret_key_base.blank?
+ ActiveSupport::Deprecation.warn "You didn't set `secret_key_base`. " +
+ "Read the upgrade documentation to learn more about this new config option."
+
+ if secrets.secret_token.blank?
+ raise "Missing `secret_key_base` for '#{Rails.env}' environment, set this value in `config/secrets.yml`"
+ end
+ end
+ end
+
+ private
+
+ def build_request(env)
+ req = super
+ env["ORIGINAL_FULLPATH"] = req.fullpath
+ env["ORIGINAL_SCRIPT_NAME"] = req.script_name
+ req
+ end
+
+ def build_middleware
+ config.app_middleware + super
+ end
+ end
+end
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
new file mode 100644
index 0000000000..9baf8aa742
--- /dev/null
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -0,0 +1,81 @@
+require "active_support/notifications"
+require "active_support/dependencies"
+require "active_support/descendants_tracker"
+
+module Rails
+ class Application
+ module Bootstrap
+ include Initializable
+
+ initializer :load_environment_hook, group: :all do end
+
+ initializer :load_active_support, group: :all do
+ require "active_support/all" unless config.active_support.bare
+ end
+
+ initializer :set_eager_load, group: :all do
+ if config.eager_load.nil?
+ warn <<-INFO
+config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
+
+ * development - set it to false
+ * test - set it to false (unless you use a tool that preloads your test environment)
+ * production - set it to true
+
+INFO
+ config.eager_load = config.cache_classes
+ end
+ end
+
+ # Initialize the logger early in the stack in case we need to log some deprecation.
+ initializer :initialize_logger, group: :all do
+ Rails.logger ||= config.logger || begin
+ path = config.paths["log"].first
+ unless File.exist? File.dirname path
+ FileUtils.mkdir_p File.dirname path
+ end
+
+ f = File.open path, 'a'
+ f.binmode
+ f.sync = config.autoflush_log # if true make sure every write flushes
+
+ logger = ActiveSupport::Logger.new f
+ logger.formatter = config.log_formatter
+ logger = ActiveSupport::TaggedLogging.new(logger)
+ logger
+ rescue StandardError
+ logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR))
+ logger.level = ActiveSupport::Logger::WARN
+ logger.warn(
+ "Rails Error: Unable to access log file. Please ensure that #{path} exists and is writable " +
+ "(ie, make it writable for user and group: chmod 0664 #{path}). " +
+ "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
+ )
+ logger
+ end
+
+ Rails.logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase)
+ end
+
+ # Initialize cache early in the stack so railties can make use of it.
+ initializer :initialize_cache, group: :all do
+ unless Rails.cache
+ Rails.cache = ActiveSupport::Cache.lookup_store(config.cache_store)
+
+ if Rails.cache.respond_to?(:middleware)
+ config.middleware.insert_before(::Rack::Runtime, Rails.cache.middleware)
+ end
+ end
+ end
+
+ # Sets the dependency loading mechanism.
+ initializer :initialize_dependency_mechanism, group: :all do
+ ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
+ end
+
+ initializer :bootstrap_hook, group: :all do |app|
+ ActiveSupport.run_load_hooks(:before_initialize, app)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
new file mode 100644
index 0000000000..65cff1561a
--- /dev/null
+++ b/railties/lib/rails/application/configuration.rb
@@ -0,0 +1,211 @@
+require 'active_support/core_ext/kernel/reporting'
+require 'active_support/file_update_checker'
+require 'rails/engine/configuration'
+require 'rails/source_annotation_extractor'
+
+require 'active_support/deprecation'
+require 'active_support/core_ext/string/strip' # for strip_heredoc
+
+module Rails
+ class Application
+ class Configuration < ::Rails::Engine::Configuration
+ attr_accessor :allow_concurrency, :asset_host, :autoflush_log,
+ :cache_classes, :cache_store, :consider_all_requests_local, :console,
+ :eager_load, :exceptions_app, :file_watcher, :filter_parameters,
+ :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
+ :railties_order, :relative_url_root, :secret_key_base, :secret_token,
+ :ssl_options, :public_file_server,
+ :session_options, :time_zone, :reload_classes_only_on_change,
+ :beginning_of_week, :filter_redirect, :x
+
+ attr_writer :log_level
+ attr_reader :encoding, :api_only, :static_cache_control
+
+ def initialize(*)
+ super
+ self.encoding = "utf-8"
+ @allow_concurrency = nil
+ @consider_all_requests_local = false
+ @filter_parameters = []
+ @filter_redirect = []
+ @helpers_paths = []
+ @public_file_server = ActiveSupport::OrderedOptions.new
+ @public_file_server.enabled = true
+ @public_file_server.index_name = "index"
+ @force_ssl = false
+ @ssl_options = {}
+ @session_store = :cookie_store
+ @session_options = {}
+ @time_zone = "UTC"
+ @beginning_of_week = :monday
+ @log_level = nil
+ @generators = app_generators
+ @cache_store = [ :file_store, "#{root}/tmp/cache/" ]
+ @railties_order = [:all]
+ @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"]
+ @reload_classes_only_on_change = true
+ @file_watcher = ActiveSupport::FileUpdateChecker
+ @exceptions_app = nil
+ @autoflush_log = true
+ @log_formatter = ActiveSupport::Logger::SimpleFormatter.new
+ @eager_load = nil
+ @secret_token = nil
+ @secret_key_base = nil
+ @api_only = false
+ @debug_exception_response_format = nil
+ @x = Custom.new
+ end
+
+ def static_cache_control=(value)
+ ActiveSupport::Deprecation.warn <<-eow.strip_heredoc
+ `static_cache_control` is deprecated and will be removed in Rails 5.1.
+ Please use
+ `config.public_file_server.headers = { 'Cache-Control' => '#{value}' }`
+ instead.
+ eow
+
+ @static_cache_control = value
+ end
+
+ def serve_static_files
+ ActiveSupport::Deprecation.warn <<-eow.strip_heredoc
+ `serve_static_files` is deprecated and will be removed in Rails 5.1.
+ Please use `public_file_server.enabled` instead.
+ eow
+
+ @public_file_server.enabled
+ end
+
+ def serve_static_files=(value)
+ ActiveSupport::Deprecation.warn <<-eow.strip_heredoc
+ `serve_static_files` is deprecated and will be removed in Rails 5.1.
+ Please use `public_file_server.enabled = #{value}` instead.
+ eow
+
+ @public_file_server.enabled = value
+ end
+
+ def encoding=(value)
+ @encoding = value
+ silence_warnings do
+ Encoding.default_external = value
+ Encoding.default_internal = value
+ end
+ end
+
+ def api_only=(value)
+ @api_only = value
+ generators.api_only = value
+
+ @debug_exception_response_format ||= :api
+ end
+
+ def debug_exception_response_format
+ @debug_exception_response_format || :default
+ end
+
+ def debug_exception_response_format=(value)
+ @debug_exception_response_format = value
+ end
+
+ def paths
+ @paths ||= begin
+ paths = super
+ paths.add "config/database", with: "config/database.yml"
+ paths.add "config/secrets", with: "config/secrets.yml"
+ paths.add "config/environment", with: "config/environment.rb"
+ paths.add "lib/templates"
+ paths.add "log", with: "log/#{Rails.env}.log"
+ paths.add "public"
+ paths.add "public/javascripts"
+ paths.add "public/stylesheets"
+ paths.add "tmp"
+ paths
+ end
+ end
+
+ # Loads and returns the entire raw configuration of database from
+ # values stored in `config/database.yml`.
+ def database_configuration
+ path = paths["config/database"].existent.first
+ yaml = Pathname.new(path) if path
+
+ config = if yaml && yaml.exist?
+ require "yaml"
+ require "erb"
+ YAML.load(ERB.new(yaml.read).result) || {}
+ elsif ENV['DATABASE_URL']
+ # Value from ENV['DATABASE_URL'] is set to default database connection
+ # by Active Record.
+ {}
+ else
+ raise "Could not load database configuration. No such file - #{paths["config/database"].instance_variable_get(:@paths)}"
+ end
+
+ config
+ rescue Psych::SyntaxError => e
+ raise "YAML syntax error occurred while parsing #{paths["config/database"].first}. " \
+ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
+ "Error: #{e.message}"
+ rescue => e
+ 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
+
+ def colorize_logging=(val)
+ ActiveSupport::LogSubscriber.colorize_logging = val
+ self.generators.colorize_logging = val
+ end
+
+ def session_store(*args)
+ if args.empty?
+ case @session_store
+ when :disabled
+ nil
+ when :active_record_store
+ begin
+ ActionDispatch::Session::ActiveRecordStore
+ rescue NameError
+ raise "`ActiveRecord::SessionStore` is extracted out of Rails into a gem. " \
+ "Please add `activerecord-session_store` to your Gemfile to use it."
+ end
+ when Symbol
+ ActionDispatch::Session.const_get(@session_store.to_s.camelize)
+ else
+ @session_store
+ end
+ else
+ @session_store = args.shift
+ @session_options = args.shift || {}
+ end
+ end
+
+ def annotations
+ SourceAnnotationExtractor::Annotation
+ end
+
+ class Custom #:nodoc:
+ def initialize
+ @configurations = Hash.new
+ end
+
+ def method_missing(method, *args)
+ if method =~ /=$/
+ @configurations[$`.to_sym] = args.first
+ else
+ @configurations.fetch(method) {
+ @configurations[method] = ActiveSupport::OrderedOptions.new
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
new file mode 100644
index 0000000000..ed6a1f82d3
--- /dev/null
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -0,0 +1,117 @@
+module Rails
+ class Application
+ class DefaultMiddlewareStack
+ attr_reader :config, :paths, :app
+
+ def initialize(app, config, paths)
+ @app = app
+ @config = config
+ @paths = paths
+ end
+
+ def build_stack
+ ActionDispatch::MiddlewareStack.new.tap do |middleware|
+ if config.force_ssl
+ middleware.use ::ActionDispatch::SSL, config.ssl_options
+ end
+
+ middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
+
+ if config.public_file_server.enabled
+ headers = config.public_file_server.headers || {}
+ headers['Cache-Control'.freeze] = config.static_cache_control if config.static_cache_control
+
+ middleware.use ::ActionDispatch::Static, paths["public"].first, index: config.public_file_server.index_name, headers: headers
+ end
+
+ if rack_cache = load_rack_cache
+ require "action_dispatch/http/rack_cache"
+ middleware.use ::Rack::Cache, rack_cache
+ end
+
+ if config.allow_concurrency == false
+ # User has explicitly opted out of concurrent request
+ # handling: presumably their code is not threadsafe
+
+ middleware.use ::Rack::Lock
+
+ elsif config.allow_concurrency == :unsafe
+ # Do nothing, even if we know this is dangerous. This is the
+ # historical behaviour for true.
+
+ else
+ # Default concurrency setting: enabled, but safe
+
+ unless config.cache_classes && config.eager_load
+ # Without cache_classes + eager_load, the load interlock
+ # is required for proper operation
+
+ middleware.use ::ActionDispatch::LoadInterlock
+ end
+ end
+
+ middleware.use ::Rack::Runtime
+ middleware.use ::Rack::MethodOverride unless config.api_only
+ middleware.use ::ActionDispatch::RequestId
+
+ # Must come after Rack::MethodOverride to properly log overridden methods
+ middleware.use ::Rails::Rack::Logger, config.log_tags
+ middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
+ middleware.use ::ActionDispatch::DebugExceptions, app, config.debug_exception_response_format
+ middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
+
+ unless config.cache_classes
+ middleware.use ::ActionDispatch::Reloader, lambda { reload_dependencies? }
+ end
+
+ middleware.use ::ActionDispatch::Callbacks
+ middleware.use ::ActionDispatch::Cookies unless config.api_only
+
+ if !config.api_only && config.session_store
+ if config.force_ssl && !config.session_options.key?(:secure)
+ config.session_options[:secure] = true
+ end
+ middleware.use config.session_store, config.session_options
+ middleware.use ::ActionDispatch::Flash
+ end
+
+ middleware.use ::Rack::Head
+ middleware.use ::Rack::ConditionalGet
+ middleware.use ::Rack::ETag, "no-cache"
+ end
+ end
+
+ private
+
+ def reload_dependencies?
+ config.reload_classes_only_on_change != true || app.reloaders.map(&:updated?).any?
+ end
+
+ def load_rack_cache
+ rack_cache = config.action_dispatch.rack_cache
+ return unless rack_cache
+
+ begin
+ require 'rack/cache'
+ rescue LoadError => error
+ error.message << ' Be sure to add rack-cache to your Gemfile'
+ raise
+ end
+
+ if rack_cache == true
+ {
+ metastore: "rails:/",
+ entitystore: "rails:/",
+ verbose: false
+ }
+ else
+ rack_cache
+ end
+ end
+
+ def show_exceptions_app
+ config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
new file mode 100644
index 0000000000..404e3c3e23
--- /dev/null
+++ b/railties/lib/rails/application/finisher.rb
@@ -0,0 +1,122 @@
+module Rails
+ class Application
+ module Finisher
+ include Initializable
+
+ initializer :add_generator_templates do
+ config.generators.templates.unshift(*paths["lib/templates"].existent)
+ end
+
+ initializer :ensure_autoload_once_paths_as_subset do
+ extra = ActiveSupport::Dependencies.autoload_once_paths -
+ ActiveSupport::Dependencies.autoload_paths
+
+ unless extra.empty?
+ abort <<-end_error
+ autoload_once_paths must be a subset of the autoload_paths.
+ Extra items in autoload_once_paths: #{extra * ','}
+ end_error
+ end
+ end
+
+ initializer :add_builtin_route do |app|
+ if Rails.env.development?
+ app.routes.append do
+ get '/rails/info/properties' => "rails/info#properties"
+ get '/rails/info/routes' => "rails/info#routes"
+ get '/rails/info' => "rails/info#index"
+ get '/' => "rails/welcome#index"
+ end
+ end
+ end
+
+ initializer :build_middleware_stack do
+ build_middleware_stack
+ end
+
+ initializer :define_main_app_helper do |app|
+ app.routes.define_mounted_helper(:main_app)
+ end
+
+ initializer :add_to_prepare_blocks do
+ config.to_prepare_blocks.each do |block|
+ ActionDispatch::Reloader.to_prepare(&block)
+ end
+ end
+
+ # This needs to happen before eager load so it happens
+ # in exactly the same point regardless of config.cache_classes
+ initializer :run_prepare_callbacks do
+ ActionDispatch::Reloader.prepare!
+ end
+
+ initializer :eager_load! do
+ if config.eager_load
+ ActiveSupport.run_load_hooks(:before_eager_load, self)
+ config.eager_load_namespaces.each(&:eager_load!)
+ end
+ end
+
+ # All initialization is done, including eager loading in production
+ initializer :finisher_hook do
+ ActiveSupport.run_load_hooks(:after_initialize, self)
+ end
+
+ # Set routes reload after the finisher hook to ensure routes added in
+ # the hook are taken into account.
+ initializer :set_routes_reloader_hook do
+ reloader = routes_reloader
+ reloader.execute_if_updated
+ self.reloaders << reloader
+ ActionDispatch::Reloader.to_prepare do
+ # We configure #execute rather than #execute_if_updated because if
+ # autoloaded constants are cleared we need to reload routes also in
+ # case any was used there, as in
+ #
+ # mount MailPreview => 'mail_view'
+ #
+ # This means routes are also reloaded if i18n is updated, which
+ # might not be necessary, but in order to be more precise we need
+ # some sort of reloaders dependency support, to be added.
+ reloader.execute
+ end
+ end
+
+ # Set clearing dependencies after the finisher hook to ensure paths
+ # added in the hook are taken into account.
+ initializer :set_clear_dependencies_hook, group: :all do
+ callback = lambda do
+ ActiveSupport::Dependencies.interlock.attempt_unloading do
+ ActiveSupport::DescendantsTracker.clear
+ ActiveSupport::Dependencies.clear
+ end
+ end
+
+ if config.reload_classes_only_on_change
+ reloader = config.file_watcher.new(*watchable_args, &callback)
+ self.reloaders << reloader
+
+ # Prepend this callback to have autoloaded constants cleared before
+ # any other possible reloading, in case they need to autoload fresh
+ # constants.
+ ActionDispatch::Reloader.to_prepare(prepend: true) do
+ # In addition to changes detected by the file watcher, if routes
+ # or i18n have been updated we also need to clear constants,
+ # that's why we run #execute rather than #execute_if_updated, this
+ # callback has to clear autoloaded constants after any update.
+ reloader.execute
+ end
+ else
+ ActionDispatch::Reloader.to_cleanup(&callback)
+ end
+ end
+
+ # Disable dependency loading during request cycle
+ initializer :disable_dependency_loading do
+ if config.eager_load && config.cache_classes
+ ActiveSupport::Dependencies.unhook!
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
new file mode 100644
index 0000000000..cf0a4e128f
--- /dev/null
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -0,0 +1,54 @@
+require "active_support/core_ext/module/delegation"
+
+module Rails
+ class Application
+ class RoutesReloader
+ attr_reader :route_sets, :paths
+ delegate :execute_if_updated, :execute, :updated?, to: :updater
+
+ def initialize
+ @paths = []
+ @route_sets = []
+ end
+
+ def reload!
+ clear!
+ load_paths
+ finalize!
+ ensure
+ revert
+ end
+
+ private
+
+ def updater
+ @updater ||= begin
+ updater = ActiveSupport::FileUpdateChecker.new(paths) { reload! }
+ updater.execute
+ updater
+ end
+ end
+
+ def clear!
+ route_sets.each do |routes|
+ routes.disable_clear_and_finalize = true
+ routes.clear!
+ end
+ end
+
+ def load_paths
+ paths.each { |path| load(path) }
+ end
+
+ def finalize!
+ route_sets.each(&:finalize!)
+ end
+
+ def revert
+ route_sets.each do |routes|
+ routes.disable_clear_and_finalize = false
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/application_controller.rb b/railties/lib/rails/application_controller.rb
new file mode 100644
index 0000000000..618a09a5b3
--- /dev/null
+++ b/railties/lib/rails/application_controller.rb
@@ -0,0 +1,16 @@
+class Rails::ApplicationController < ActionController::Base # :nodoc:
+ self.view_paths = File.expand_path('../templates', __FILE__)
+ layout 'application'
+
+ protected
+
+ def require_local!
+ unless local_request?
+ render html: '<p>For security purposes, this information is only available to local requests.</p>'.html_safe, status: :forbidden
+ end
+ end
+
+ def local_request?
+ Rails.application.config.consider_all_requests_local || request.local?
+ end
+end
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
new file mode 100644
index 0000000000..5276eb33c9
--- /dev/null
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -0,0 +1,32 @@
+require 'active_support/backtrace_cleaner'
+
+module Rails
+ class BacktraceCleaner < ActiveSupport::BacktraceCleaner
+ APP_DIRS_PATTERN = /^\/?(app|config|lib|test)/
+ RENDER_TEMPLATE_PATTERN = /:in `_render_template_\w*'/
+ EMPTY_STRING = ''.freeze
+ SLASH = '/'.freeze
+ DOT_SLASH = './'.freeze
+
+ def initialize
+ super
+ @root = "#{Rails.root}/".freeze
+ add_filter { |line| line.sub(@root, EMPTY_STRING) }
+ add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, EMPTY_STRING) }
+ add_filter { |line| line.sub(DOT_SLASH, SLASH) } # for tests
+
+ add_gem_filters
+ add_silencer { |line| line !~ APP_DIRS_PATTERN }
+ end
+
+ private
+ def add_gem_filters
+ gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
+ return if gems_paths.empty?
+
+ gems_regexp = %r{(#{gems_paths.join('|')})/gems/([^/]+)-([\w.]+)/(.*)}
+ gems_result = '\2 (\3) \4'.freeze
+ add_filter { |line| line.sub(gems_regexp, gems_result) }
+ end
+ end
+end
diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb
new file mode 100644
index 0000000000..a8794bc0de
--- /dev/null
+++ b/railties/lib/rails/cli.rb
@@ -0,0 +1,15 @@
+require 'rails/app_loader'
+
+# If we are inside a Rails application this method performs an exec and thus
+# the rest of this script is not run.
+Rails::AppLoader.exec_app
+
+require 'rails/ruby_version_check'
+Signal.trap("INT") { puts; exit(1) }
+
+if ARGV.first == 'plugin'
+ ARGV.shift
+ require 'rails/commands/plugin'
+else
+ require 'rails/commands/application'
+end
diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb
new file mode 100644
index 0000000000..8e9097e1ef
--- /dev/null
+++ b/railties/lib/rails/code_statistics.rb
@@ -0,0 +1,100 @@
+require 'rails/code_statistics_calculator'
+
+class CodeStatistics #:nodoc:
+
+ TEST_TYPES = ['Controller tests',
+ 'Helper tests',
+ 'Model tests',
+ 'Mailer tests',
+ 'Job tests',
+ 'Integration tests']
+
+ def initialize(*pairs)
+ @pairs = pairs
+ @statistics = calculate_statistics
+ @total = calculate_total if pairs.length > 1
+ end
+
+ def to_s
+ print_header
+ @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
+ print_splitter
+
+ if @total
+ print_line("Total", @total)
+ print_splitter
+ end
+
+ print_code_test_stats
+ end
+
+ private
+ def calculate_statistics
+ Hash[@pairs.map{|pair| [pair.first, calculate_directory_statistics(pair.last)]}]
+ end
+
+ def calculate_directory_statistics(directory, pattern = /.*\.(rb|js|coffee|rake)$/)
+ stats = CodeStatisticsCalculator.new
+
+ Dir.foreach(directory) do |file_name|
+ path = "#{directory}/#{file_name}"
+
+ if File.directory?(path) && (/^\./ !~ file_name)
+ stats.add(calculate_directory_statistics(path, pattern))
+ elsif file_name =~ pattern
+ stats.add_by_file_path(path)
+ end
+ end
+
+ stats
+ end
+
+ def calculate_total
+ @statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, total|
+ total.add(pair.last)
+ end
+ end
+
+ def calculate_code
+ code_loc = 0
+ @statistics.each { |k, v| code_loc += v.code_lines unless TEST_TYPES.include? k }
+ code_loc
+ end
+
+ def calculate_tests
+ test_loc = 0
+ @statistics.each { |k, v| test_loc += v.code_lines if TEST_TYPES.include? k }
+ test_loc
+ end
+
+ def print_header
+ print_splitter
+ puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
+ print_splitter
+ end
+
+ def print_splitter
+ puts "+----------------------+--------+--------+---------+---------+-----+-------+"
+ end
+
+ def print_line(name, statistics)
+ m_over_c = (statistics.methods / statistics.classes) rescue m_over_c = 0
+ loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue loc_over_m = 0
+
+ puts "| #{name.ljust(20)} " \
+ "| #{statistics.lines.to_s.rjust(6)} " \
+ "| #{statistics.code_lines.to_s.rjust(6)} " \
+ "| #{statistics.classes.to_s.rjust(7)} " \
+ "| #{statistics.methods.to_s.rjust(7)} " \
+ "| #{m_over_c.to_s.rjust(3)} " \
+ "| #{loc_over_m.to_s.rjust(5)} |"
+ end
+
+ def print_code_test_stats
+ code = calculate_code
+ tests = calculate_tests
+
+ puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
+ puts ""
+ end
+end
diff --git a/railties/lib/rails/code_statistics_calculator.rb b/railties/lib/rails/code_statistics_calculator.rb
new file mode 100644
index 0000000000..fad13e8517
--- /dev/null
+++ b/railties/lib/rails/code_statistics_calculator.rb
@@ -0,0 +1,86 @@
+class CodeStatisticsCalculator #:nodoc:
+ attr_reader :lines, :code_lines, :classes, :methods
+
+ PATTERNS = {
+ rb: {
+ line_comment: /^\s*#/,
+ begin_block_comment: /^=begin/,
+ end_block_comment: /^=end/,
+ class: /^\s*class\s+[_A-Z]/,
+ method: /^\s*def\s+[_a-z]/,
+ },
+ js: {
+ line_comment: %r{^\s*//},
+ begin_block_comment: %r{^\s*/\*},
+ end_block_comment: %r{\*/},
+ method: /function(\s+[_a-zA-Z][\da-zA-Z]*)?\s*\(/,
+ },
+ coffee: {
+ line_comment: /^\s*#/,
+ begin_block_comment: /^\s*###/,
+ end_block_comment: /^\s*###/,
+ class: /^\s*class\s+[_A-Z]/,
+ method: /[-=]>/,
+ }
+ }
+
+ PATTERNS[:minitest] = PATTERNS[:rb].merge method: /^\s*(def|test)\s+['"_a-z]/
+ PATTERNS[:rake] = PATTERNS[:rb]
+
+ def initialize(lines = 0, code_lines = 0, classes = 0, methods = 0)
+ @lines = lines
+ @code_lines = code_lines
+ @classes = classes
+ @methods = methods
+ end
+
+ def add(code_statistics_calculator)
+ @lines += code_statistics_calculator.lines
+ @code_lines += code_statistics_calculator.code_lines
+ @classes += code_statistics_calculator.classes
+ @methods += code_statistics_calculator.methods
+ end
+
+ def add_by_file_path(file_path)
+ File.open(file_path) do |f|
+ self.add_by_io(f, file_type(file_path))
+ end
+ end
+
+ def add_by_io(io, file_type)
+ patterns = PATTERNS[file_type] || {}
+
+ comment_started = false
+
+ while line = io.gets
+ @lines += 1
+
+ if comment_started
+ if patterns[:end_block_comment] && line =~ patterns[:end_block_comment]
+ comment_started = false
+ end
+ next
+ else
+ if patterns[:begin_block_comment] && line =~ patterns[:begin_block_comment]
+ comment_started = true
+ next
+ end
+ end
+
+ @classes += 1 if patterns[:class] && line =~ patterns[:class]
+ @methods += 1 if patterns[:method] && line =~ patterns[:method]
+ if line !~ /^\s*$/ && (patterns[:line_comment].nil? || line !~ patterns[:line_comment])
+ @code_lines += 1
+ end
+ end
+ end
+
+ private
+ def file_type(file_path)
+ if file_path.end_with? '_test.rb'
+ :minitest
+ else
+ File.extname(file_path).sub(/\A\./, '').downcase.to_sym
+ end
+ end
+end
diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb
new file mode 100644
index 0000000000..f7753cbb83
--- /dev/null
+++ b/railties/lib/rails/command.rb
@@ -0,0 +1,70 @@
+require 'rails/commands/commands_tasks'
+
+module Rails
+ class Command #:nodoc:
+ attr_reader :argv
+
+ def initialize(argv = [])
+ @argv = argv
+
+ @option_parser = build_option_parser
+ @options = {}
+ end
+
+ def self.run(task_name, argv)
+ command_name = command_name_for(task_name)
+
+ if command = command_for(command_name)
+ command.new(argv).run(command_name)
+ else
+ Rails::CommandsTasks.new(argv).run_command!(task_name)
+ end
+ end
+
+ def run(command_name)
+ parse_options_for(command_name)
+ @option_parser.parse! @argv
+
+ public_send(command_name)
+ end
+
+ def self.options_for(command_name, &options_to_parse)
+ @@command_options[command_name] = options_to_parse
+ end
+
+ def self.set_banner(command_name, banner)
+ options_for(command_name) { |opts, _| opts.banner = banner }
+ end
+
+ private
+ @@commands = []
+ @@command_options = {}
+
+ def parse_options_for(command_name)
+ @@command_options.fetch(command_name, proc {}).call(@option_parser, @options)
+ end
+
+ def build_option_parser
+ OptionParser.new do |opts|
+ opts.on('-h', '--help', 'Show this help.') do
+ puts opts
+ exit
+ end
+ end
+ end
+
+ def self.inherited(command)
+ @@commands << command
+ end
+
+ def self.command_name_for(task_name)
+ task_name.gsub(':', '_').to_sym
+ end
+
+ def self.command_for(command_name)
+ @@commands.find do |command|
+ command.public_instance_methods.include?(command_name)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
new file mode 100644
index 0000000000..7627fcf5a0
--- /dev/null
+++ b/railties/lib/rails/commands.rb
@@ -0,0 +1,19 @@
+ARGV << '--help' if ARGV.empty?
+
+aliases = {
+ "g" => "generate",
+ "d" => "destroy",
+ "c" => "console",
+ "s" => "server",
+ "db" => "dbconsole",
+ "r" => "runner",
+ "t" => "test"
+}
+
+command = ARGV.shift
+command = aliases[command] || command
+
+require 'rails/command'
+require 'rails/commands/dev_cache'
+
+Rails::Command.run(command, ARGV)
diff --git a/railties/lib/rails/commands/application.rb b/railties/lib/rails/commands/application.rb
new file mode 100644
index 0000000000..c998e6b6a8
--- /dev/null
+++ b/railties/lib/rails/commands/application.rb
@@ -0,0 +1,17 @@
+require 'rails/generators'
+require 'rails/generators/rails/app/app_generator'
+
+module Rails
+ module Generators
+ class AppGenerator # :nodoc:
+ # We want to exit on failure to be kind to other libraries
+ # This is only when accessing via CLI
+ def self.exit_on_failure?
+ true
+ end
+ end
+ end
+end
+
+args = Rails::Generators::ARGVScrubber.new(ARGV).prepare!
+Rails::Generators::AppGenerator.start args
diff --git a/railties/lib/rails/commands/commands_tasks.rb b/railties/lib/rails/commands/commands_tasks.rb
new file mode 100644
index 0000000000..da3b9452d5
--- /dev/null
+++ b/railties/lib/rails/commands/commands_tasks.rb
@@ -0,0 +1,180 @@
+require 'rails/commands/rake_proxy'
+
+module Rails
+ # This is a class which takes in a rails command and initiates the appropriate
+ # initiation sequence.
+ #
+ # Warning: This class mutates ARGV because some commands require manipulating
+ # it before they are run.
+ class CommandsTasks # :nodoc:
+ include Rails::RakeProxy
+
+ attr_reader :argv
+
+ HELP_MESSAGE = <<-EOT
+Usage: rails COMMAND [ARGS]
+
+The most common rails commands are:
+ generate Generate new code (short-cut alias: "g")
+ console Start the Rails console (short-cut alias: "c")
+ server Start the Rails server (short-cut alias: "s")
+ test Run tests (short-cut alias: "t")
+ dbconsole Start a console for the database specified in config/database.yml
+ (short-cut alias: "db")
+ new Create a new Rails application. "rails new my_app" creates a
+ new application called MyApp in "./my_app"
+
+All commands can be run with -h (or --help) for more information.
+
+In addition to those commands, there are:
+EOT
+
+ ADDITIONAL_COMMANDS = [
+ [ 'destroy', 'Undo code generated with "generate" (short-cut alias: "d")' ],
+ [ 'plugin new', 'Generates skeleton for developing a Rails plugin' ],
+ [ 'runner',
+ 'Run a piece of code in the application environment (short-cut alias: "r")' ]
+ ]
+
+ COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole runner new version help test)
+
+ def initialize(argv)
+ @argv = argv
+ end
+
+ def run_command!(command)
+ command = parse_command(command)
+
+ if COMMAND_WHITELIST.include?(command)
+ send(command)
+ else
+ run_rake_task(command)
+ end
+ end
+
+ def plugin
+ require_command!("plugin")
+ end
+
+ def generate
+ generate_or_destroy(:generate)
+ end
+
+ def destroy
+ generate_or_destroy(:destroy)
+ end
+
+ def console
+ require_command!("console")
+ options = Rails::Console.parse_arguments(argv)
+
+ # RAILS_ENV needs to be set before config/application is required
+ ENV['RAILS_ENV'] = options[:environment] if options[:environment]
+
+ # shift ARGV so IRB doesn't freak
+ shift_argv!
+
+ require_application_and_environment!
+ Rails::Console.start(Rails.application, options)
+ end
+
+ def server
+ set_application_directory!
+ require_command!("server")
+
+ Rails::Server.new.tap do |server|
+ # We need to require application after the server sets environment,
+ # otherwise the --environment option given to the server won't propagate.
+ require APP_PATH
+ Dir.chdir(Rails.application.root)
+ server.start
+ end
+ end
+
+ def test
+ require_command!("test")
+ end
+
+ def dbconsole
+ require_command!("dbconsole")
+ Rails::DBConsole.start
+ end
+
+ def runner
+ require_command!("runner")
+ end
+
+ def new
+ if %w(-h --help).include?(argv.first)
+ require_command!("application")
+ else
+ exit_with_initialization_warning!
+ end
+ end
+
+ def version
+ argv.unshift '--version'
+ require_command!("application")
+ end
+
+ def help
+ write_help_message
+ write_commands ADDITIONAL_COMMANDS + formatted_rake_tasks
+ end
+
+ private
+
+ def exit_with_initialization_warning!
+ puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
+ puts "Type 'rails' for help."
+ exit(1)
+ end
+
+ def shift_argv!
+ argv.shift if argv.first && argv.first[0] != '-'
+ end
+
+ def require_command!(command)
+ require "rails/commands/#{command}"
+ end
+
+ def generate_or_destroy(command)
+ require 'rails/generators'
+ require_application_and_environment!
+ Rails.application.load_generators
+ require_command!(command)
+ end
+
+ # Change to the application's path if there is no config.ru file in current directory.
+ # This allows us to run `rails server` from other directories, but still get
+ # the main config.ru and properly set the tmp directory.
+ def set_application_directory!
+ Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
+ end
+
+ def require_application_and_environment!
+ require APP_PATH
+ Rails.application.require_environment!
+ end
+
+ def write_help_message
+ puts HELP_MESSAGE
+ end
+
+ def write_commands(commands)
+ width = commands.map { |name, _| name.size }.max || 10
+ commands.each { |command| printf(" %-#{width}s %s\n", *command) }
+ end
+
+ def parse_command(command)
+ case command
+ when '--version', '-v'
+ 'version'
+ when '--help', '-h'
+ 'help'
+ else
+ command
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb
new file mode 100644
index 0000000000..ea5d20ea24
--- /dev/null
+++ b/railties/lib/rails/commands/console.rb
@@ -0,0 +1,68 @@
+require 'optparse'
+require 'irb'
+require 'irb/completion'
+require 'rails/commands/console_helper'
+
+module Rails
+ class Console
+ include ConsoleHelper
+
+ class << self
+ def parse_arguments(arguments)
+ options = {}
+
+ OptionParser.new do |opt|
+ opt.banner = "Usage: rails console [environment] [options]"
+ opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
+ opt.on("-e", "--environment=name", String,
+ "Specifies the environment to run this console under (test/development/production).",
+ "Default: development") { |v| options[:environment] = v.strip }
+ opt.parse!(arguments)
+ end
+
+ set_options_env(arguments, options)
+ end
+ end
+
+ attr_reader :options, :app, :console
+
+ def initialize(app, options={})
+ @app = app
+ @options = options
+
+ app.sandbox = sandbox?
+ app.load_console
+
+ @console = app.config.console || IRB
+ end
+
+ def sandbox?
+ options[:sandbox]
+ end
+
+ def environment
+ options[:environment] ||= super
+ end
+ alias_method :environment?, :environment
+
+ def set_environment!
+ Rails.env = environment
+ end
+
+ def start
+ set_environment! if environment?
+
+ if sandbox?
+ puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})"
+ puts "Any modifications you make will be rolled back on exit"
+ else
+ puts "Loading #{Rails.env} environment (Rails #{Rails.version})"
+ end
+
+ if defined?(console::ExtendCommandBundle)
+ console::ExtendCommandBundle.include(Rails::ConsoleMethods)
+ end
+ console.start
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/console_helper.rb b/railties/lib/rails/commands/console_helper.rb
new file mode 100644
index 0000000000..8ee0b60012
--- /dev/null
+++ b/railties/lib/rails/commands/console_helper.rb
@@ -0,0 +1,34 @@
+require 'active_support/concern'
+
+module Rails
+ module ConsoleHelper # :nodoc:
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def start(*args)
+ new(*args).start
+ end
+
+ private
+ def set_options_env(arguments, options)
+ if arguments.first && arguments.first[0] != '-'
+ env = arguments.first
+ if available_environments.include? env
+ options[:environment] = env
+ else
+ options[:environment] = %w(production development test).detect { |e| e =~ /^#{env}/ } || env
+ end
+ end
+ options
+ end
+
+ def available_environments
+ Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') }
+ end
+ end
+
+ def environment
+ ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
+ end
+ end
+end \ No newline at end of file
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
new file mode 100644
index 0000000000..2c36edfa3f
--- /dev/null
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -0,0 +1,173 @@
+require 'erb'
+require 'yaml'
+require 'optparse'
+require 'rails/commands/console_helper'
+
+module Rails
+ class DBConsole
+ include ConsoleHelper
+
+ attr_reader :arguments
+
+ class << self
+ def parse_arguments(arguments)
+ options = {}
+
+ OptionParser.new do |opt|
+ opt.banner = "Usage: rails dbconsole [environment] [options]"
+ opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v|
+ options['include_password'] = true
+ end
+
+ opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'],
+ "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode|
+ options['mode'] = mode
+ end
+
+ opt.on("--header") do |h|
+ options['header'] = h
+ end
+
+ opt.on("-h", "--help", "Show this help message.") do
+ puts opt
+ exit
+ end
+
+ opt.on("-e", "--environment=name", String,
+ "Specifies the environment to run this console under (test/development/production).",
+ "Default: development"
+ ) { |v| options[:environment] = v.strip }
+
+ opt.parse!(arguments)
+ abort opt.to_s unless (0..1).include?(arguments.size)
+ end
+
+ set_options_env(arguments, options)
+ end
+ end
+
+ def initialize(arguments = ARGV)
+ @arguments = arguments
+ end
+
+ def start
+ options = self.class.parse_arguments(arguments)
+ ENV['RAILS_ENV'] = options[:environment] || environment
+
+ case config["adapter"]
+ when /^(jdbc)?mysql/
+ args = {
+ 'host' => '--host',
+ 'port' => '--port',
+ 'socket' => '--socket',
+ 'username' => '--user',
+ 'encoding' => '--default-character-set',
+ 'sslca' => '--ssl-ca',
+ 'sslcert' => '--ssl-cert',
+ 'sslcapath' => '--ssl-capath',
+ 'sslcipher' => '--ssl-cipher',
+ 'sslkey' => '--ssl-key'
+ }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
+
+ if config['password'] && options['include_password']
+ args << "--password=#{config['password']}"
+ elsif config['password'] && !config['password'].to_s.empty?
+ args << "-p"
+ end
+
+ args << config['database']
+
+ find_cmd_and_exec(['mysql', 'mysql5'], *args)
+
+ when /^postgres|^postgis/
+ ENV['PGUSER'] = config["username"] if config["username"]
+ ENV['PGHOST'] = config["host"] if config["host"]
+ ENV['PGPORT'] = config["port"].to_s if config["port"]
+ ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && options['include_password']
+ find_cmd_and_exec('psql', config["database"])
+
+ when "sqlite3"
+ args = []
+
+ args << "-#{options['mode']}" if options['mode']
+ args << "-header" if options['header']
+ args << File.expand_path(config['database'], Rails.respond_to?(:root) ? Rails.root : nil)
+
+ find_cmd_and_exec('sqlite3', *args)
+
+ when "oracle", "oracle_enhanced"
+ logon = ""
+
+ if config['username']
+ logon = config['username']
+ logon << "/#{config['password']}" if config['password'] && options['include_password']
+ logon << "@#{config['database']}" if config['database']
+ end
+
+ find_cmd_and_exec('sqlplus', logon)
+
+ when "sqlserver"
+ args = []
+
+ args += ["-D", "#{config['database']}"] if config['database']
+ args += ["-U", "#{config['username']}"] if config['username']
+ args += ["-P", "#{config['password']}"] if config['password']
+
+ if config['host']
+ host_arg = "#{config['host']}"
+ host_arg << ":#{config['port']}" if config['port']
+ args += ["-S", host_arg]
+ end
+
+ find_cmd_and_exec("sqsh", *args)
+
+ else
+ abort "Unknown command-line client for #{config['database']}."
+ end
+ end
+
+ def config
+ @config ||= begin
+ if configurations[environment].blank?
+ raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
+ else
+ configurations[environment]
+ end
+ end
+ end
+
+ def environment
+ Rails.respond_to?(:env) ? Rails.env : super
+ end
+
+ protected
+ def configurations
+ require APP_PATH
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
+ ActiveRecord::Base.configurations
+ end
+
+ def find_cmd_and_exec(commands, *args)
+ commands = Array(commands)
+
+ dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
+ unless (ext = RbConfig::CONFIG['EXEEXT']).empty?
+ commands = commands.map{|cmd| "#{cmd}#{ext}"}
+ end
+
+ full_path_command = nil
+ found = commands.detect do |cmd|
+ dirs_on_path.detect do |path|
+ full_path_command = File.join(path, cmd)
+ File.file?(full_path_command) && File.executable?(full_path_command)
+ end
+ end
+
+ if found
+ exec full_path_command, *args
+ else
+ abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb
new file mode 100644
index 0000000000..ce26cc3fde
--- /dev/null
+++ b/railties/lib/rails/commands/destroy.rb
@@ -0,0 +1,11 @@
+require 'rails/generators'
+
+#if no argument/-h/--help is passed to rails destroy command, then
+#it generates the help associated.
+if [nil, "-h", "--help"].include?(ARGV.first)
+ Rails::Generators.help 'destroy'
+ exit
+end
+
+name = ARGV.shift
+Rails::Generators.invoke name, ARGV, behavior: :revoke, destination_root: Rails.root
diff --git a/railties/lib/rails/commands/dev_cache.rb b/railties/lib/rails/commands/dev_cache.rb
new file mode 100644
index 0000000000..ec96e8f630
--- /dev/null
+++ b/railties/lib/rails/commands/dev_cache.rb
@@ -0,0 +1,21 @@
+require 'rails/command'
+
+module Rails
+ module Commands
+ # This is a wrapper around the Rails dev:cache command
+ class DevCache < Command
+ set_banner :dev_cache, 'Toggle development mode caching on/off'
+ def dev_cache
+ if File.exist? 'tmp/caching-dev.txt'
+ File.delete 'tmp/caching-dev.txt'
+ puts 'Development mode is no longer being cached.'
+ else
+ FileUtils.touch 'tmp/caching-dev.txt'
+ puts 'Development mode is now being cached.'
+ end
+
+ FileUtils.touch 'tmp/restart.txt'
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/generate.rb b/railties/lib/rails/commands/generate.rb
new file mode 100644
index 0000000000..926c36b967
--- /dev/null
+++ b/railties/lib/rails/commands/generate.rb
@@ -0,0 +1,13 @@
+require 'rails/generators'
+
+#if no argument/-h/--help is passed to rails generate command, then
+#it generates the help associated.
+if [nil, "-h", "--help"].include?(ARGV.first)
+ Rails::Generators.help 'generate'
+ exit
+end
+
+name = ARGV.shift
+
+root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
+Rails::Generators.invoke name, ARGV, behavior: :invoke, destination_root: root
diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb
new file mode 100644
index 0000000000..52d8966ead
--- /dev/null
+++ b/railties/lib/rails/commands/plugin.rb
@@ -0,0 +1,23 @@
+if ARGV.first != "new"
+ ARGV[0] = "--help"
+else
+ ARGV.shift
+ unless ARGV.delete("--no-rc")
+ customrc = ARGV.index{ |x| x.include?("--rc=") }
+ railsrc = if customrc
+ File.expand_path(ARGV.delete_at(customrc).gsub(/--rc=/, ""))
+ else
+ File.join(File.expand_path("~"), '.railsrc')
+ end
+ if File.exist?(railsrc)
+ extra_args_string = File.read(railsrc)
+ extra_args = extra_args_string.split(/\n+/).flat_map(&:split)
+ puts "Using #{extra_args.join(" ")} from #{railsrc}"
+ ARGV.insert(1, *extra_args)
+ end
+ end
+end
+
+require 'rails/generators'
+require 'rails/generators/rails/plugin/plugin_generator'
+Rails::Generators::PluginGenerator.start
diff --git a/railties/lib/rails/commands/rake_proxy.rb b/railties/lib/rails/commands/rake_proxy.rb
new file mode 100644
index 0000000000..f7d5df6b2f
--- /dev/null
+++ b/railties/lib/rails/commands/rake_proxy.rb
@@ -0,0 +1,34 @@
+require 'rake'
+require 'active_support'
+
+module Rails
+ module RakeProxy #:nodoc:
+ private
+ def run_rake_task(command)
+ ARGV.unshift(command) # Prepend the command, so Rake knows how to run it.
+
+ Rake.application.standard_exception_handling do
+ Rake.application.init('rails')
+ Rake.application.load_rakefile
+ Rake.application.top_level
+ end
+ end
+
+ def rake_tasks
+ return @rake_tasks if defined?(@rake_tasks)
+
+ ActiveSupport::Deprecation.silence do
+ require_application_and_environment!
+ end
+
+ Rake::TaskManager.record_task_metadata = true
+ Rake.application.instance_variable_set(:@name, 'rails')
+ Rails.application.load_tasks
+ @rake_tasks = Rake.application.tasks.select(&:comment)
+ end
+
+ def formatted_rake_tasks
+ rake_tasks.map { |t| [ t.name_with_args, t.comment ] }
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
new file mode 100644
index 0000000000..86bce9b2fe
--- /dev/null
+++ b/railties/lib/rails/commands/runner.rb
@@ -0,0 +1,62 @@
+require 'optparse'
+
+options = { environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup }
+code_or_file = nil
+
+if ARGV.first.nil?
+ ARGV.push "-h"
+end
+
+ARGV.clone.options do |opts|
+ opts.banner = "Usage: rails runner [options] [<'Some.ruby(code)'> | <filename.rb>]"
+
+ opts.separator ""
+
+ opts.on("-e", "--environment=name", String,
+ "Specifies the environment for the runner to operate under (test/development/production).",
+ "Default: development") { |v| options[:environment] = v }
+
+ opts.separator ""
+
+ opts.on("-h", "--help",
+ "Show this help message.") { $stdout.puts opts; exit }
+
+ opts.separator ""
+ opts.separator "Examples: "
+
+ opts.separator " rails runner 'puts Rails.env'"
+ opts.separator " This runs the code `puts Rails.env` after loading the app"
+ opts.separator ""
+ opts.separator " rails runner path/to/filename.rb"
+ opts.separator " This runs the Ruby file located at `path/to/filename.rb` after loading the app"
+
+ if RbConfig::CONFIG['host_os'] !~ /mswin|mingw/
+ opts.separator ""
+ opts.separator "You can also use runner as a shebang line for your executables:"
+ opts.separator " -------------------------------------------------------------"
+ opts.separator " #!/usr/bin/env #{File.expand_path($0)} runner"
+ opts.separator ""
+ opts.separator " Product.all.each { |p| p.price *= 2 ; p.save! }"
+ opts.separator " -------------------------------------------------------------"
+ end
+
+ opts.order! { |o| code_or_file ||= o } rescue retry
+end
+
+ARGV.delete(code_or_file)
+
+ENV["RAILS_ENV"] = options[:environment]
+
+require APP_PATH
+Rails.application.require_environment!
+Rails.application.load_runner
+
+if code_or_file.nil?
+ $stderr.puts "Run '#{$0} -h' for help."
+ exit 1
+elsif File.exist?(code_or_file)
+ $0 = code_or_file
+ Kernel.load code_or_file
+else
+ eval(code_or_file, binding, __FILE__, __LINE__)
+end
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
new file mode 100644
index 0000000000..d3ea441f8e
--- /dev/null
+++ b/railties/lib/rails/commands/server.rb
@@ -0,0 +1,143 @@
+require 'fileutils'
+require 'optparse'
+require 'action_dispatch'
+require 'rails'
+
+module Rails
+ class Server < ::Rack::Server
+ class Options
+ def parse!(args)
+ args, options = args.dup, {}
+
+ option_parser(options).parse! args
+
+ options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development"
+ options[:server] = args.shift
+ options
+ end
+
+ private
+
+ def option_parser(options)
+ OptionParser.new do |opts|
+ opts.banner = "Usage: rails server [mongrel, thin etc] [options]"
+ opts.on("-p", "--port=port", Integer,
+ "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
+ opts.on("-b", "--binding=IP", String,
+ "Binds Rails to the specified IP.", "Default: localhost") { |v| options[:Host] = v }
+ opts.on("-c", "--config=file", String,
+ "Uses a custom rackup configuration.") { |v| options[:config] = v }
+ opts.on("-d", "--daemon", "Runs server as a Daemon.") { options[:daemonize] = true }
+ opts.on("-e", "--environment=name", String,
+ "Specifies the environment to run this server under (test/development/production).",
+ "Default: development") { |v| options[:environment] = v }
+ opts.on("-P", "--pid=pid", String,
+ "Specifies the PID file.",
+ "Default: tmp/pids/server.pid") { |v| options[:pid] = v }
+ opts.on("-C", "--[no-]dev-caching",
+ "Specifies whether to perform caching in development.",
+ "true or false") { |v| options[:caching] = v }
+
+ opts.separator ""
+
+ opts.on("-h", "--help", "Shows this help message.") { puts opts; exit }
+ end
+ end
+ end
+
+ def initialize(*)
+ super
+ set_environment
+ end
+
+ # TODO: this is no longer required but we keep it for the moment to support older config.ru files.
+ def app
+ @app ||= begin
+ app = super
+ app.respond_to?(:to_app) ? app.to_app : app
+ end
+ end
+
+ def opt_parser
+ Options.new
+ end
+
+ def set_environment
+ ENV["RAILS_ENV"] ||= options[:environment]
+ end
+
+ def start
+ print_boot_information
+ trap(:INT) { exit }
+ create_tmp_directories
+ setup_dev_caching
+ log_to_stdout if options[:log_stdout]
+
+ super
+ ensure
+ # The '-h' option calls exit before @options is set.
+ # If we call 'options' with it unset, we get double help banners.
+ puts 'Exiting' unless @options && options[:daemonize]
+ end
+
+ def middleware
+ Hash.new([])
+ end
+
+ def default_options
+ super.merge({
+ Port: ENV.fetch('PORT', 3000).to_i,
+ DoNotReverseLookup: true,
+ environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup,
+ daemonize: false,
+ caching: false,
+ pid: File.expand_path("tmp/pids/server.pid")
+ })
+ end
+
+ private
+
+ def setup_dev_caching
+ return unless options[:environment] == "development"
+
+ if options[:caching] == false
+ delete_cache_file
+ elsif options[:caching]
+ create_cache_file
+ end
+ end
+
+ def print_boot_information
+ url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}"
+ puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
+ puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
+ puts "=> Run `rails server -h` for more startup options"
+
+ puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
+ end
+
+ def create_cache_file
+ FileUtils.touch("tmp/caching-dev.txt")
+ end
+
+ def delete_cache_file
+ FileUtils.rm("tmp/caching-dev.txt") if File.exist?("tmp/caching-dev.txt")
+ end
+
+ def create_tmp_directories
+ %w(cache pids sockets).each do |dir_to_make|
+ FileUtils.mkdir_p(File.join(Rails.root, 'tmp', dir_to_make))
+ end
+ end
+
+ def log_to_stdout
+ wrapped_app # touch the app so the logger is set up
+
+ console = ActiveSupport::Logger.new($stdout)
+ console.formatter = Rails.logger.formatter
+ console.level = Rails.logger.level
+
+ Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/test.rb b/railties/lib/rails/commands/test.rb
new file mode 100644
index 0000000000..dd069f081f
--- /dev/null
+++ b/railties/lib/rails/commands/test.rb
@@ -0,0 +1,9 @@
+require "rails/test_unit/minitest_plugin"
+
+if defined?(ENGINE_ROOT)
+ $: << File.expand_path('test', ENGINE_ROOT)
+else
+ $: << File.expand_path('../../test', APP_PATH)
+end
+
+exit Minitest.run(ARGV)
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
new file mode 100644
index 0000000000..30eafd59f2
--- /dev/null
+++ b/railties/lib/rails/configuration.rb
@@ -0,0 +1,135 @@
+require 'active_support/ordered_options'
+require 'active_support/core_ext/object'
+require 'rails/paths'
+require 'rails/rack'
+
+module Rails
+ module Configuration
+ # MiddlewareStackProxy is a proxy for the Rails middleware stack that allows
+ # you to configure middlewares in your application. It works basically as a
+ # command recorder, saving each command to be applied after initialization
+ # over the default middleware stack, so you can add, swap, or remove any
+ # middleware in Rails.
+ #
+ # You can add your own middlewares by using the +config.middleware.use+ method:
+ #
+ # config.middleware.use Magical::Unicorns
+ #
+ # This will put the <tt>Magical::Unicorns</tt> middleware on the end of the stack.
+ # You can use +insert_before+ if you wish to add a middleware before another:
+ #
+ # config.middleware.insert_before Rack::Head, Magical::Unicorns
+ #
+ # There's also +insert_after+ which will insert a middleware after another:
+ #
+ # config.middleware.insert_after Rack::Head, Magical::Unicorns
+ #
+ # Middlewares can also be completely swapped out and replaced with others:
+ #
+ # config.middleware.swap ActionDispatch::Flash, Magical::Unicorns
+ #
+ # And finally they can also be removed from the stack completely:
+ #
+ # config.middleware.delete ActionDispatch::Flash
+ #
+ class MiddlewareStackProxy
+ def initialize(operations = [], delete_operations = [])
+ @operations = operations
+ @delete_operations = delete_operations
+ end
+
+ def insert_before(*args, &block)
+ @operations << [__method__, args, block]
+ end
+
+ alias :insert :insert_before
+
+ def insert_after(*args, &block)
+ @operations << [__method__, args, block]
+ end
+
+ def swap(*args, &block)
+ @operations << [__method__, args, block]
+ end
+
+ def use(*args, &block)
+ @operations << [__method__, args, block]
+ end
+
+ def delete(*args, &block)
+ @delete_operations << [__method__, args, block]
+ end
+
+ def unshift(*args, &block)
+ @operations << [__method__, args, block]
+ end
+
+ def merge_into(other) #:nodoc:
+ (@operations + @delete_operations).each do |operation, args, block|
+ other.send(operation, *args, &block)
+ end
+
+ other
+ end
+
+ def +(other) # :nodoc:
+ MiddlewareStackProxy.new(@operations + other.operations, @delete_operations + other.delete_operations)
+ end
+
+ protected
+ def operations
+ @operations
+ end
+
+ def delete_operations
+ @delete_operations
+ end
+ end
+
+ class Generators #:nodoc:
+ attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging, :api_only
+ attr_reader :hidden_namespaces
+
+ def initialize
+ @aliases = Hash.new { |h,k| h[k] = {} }
+ @options = Hash.new { |h,k| h[k] = {} }
+ @fallbacks = {}
+ @templates = []
+ @colorize_logging = true
+ @api_only = false
+ @hidden_namespaces = []
+ end
+
+ def initialize_copy(source)
+ @aliases = @aliases.deep_dup
+ @options = @options.deep_dup
+ @fallbacks = @fallbacks.deep_dup
+ @templates = @templates.dup
+ end
+
+ def hide_namespace(namespace)
+ @hidden_namespaces << namespace
+ end
+
+ def method_missing(method, *args)
+ method = method.to_s.sub(/=$/, '').to_sym
+
+ return @options[method] if args.empty?
+
+ if method == :rails || args.first.is_a?(Hash)
+ namespace, configuration = method, args.shift
+ else
+ namespace, configuration = args.shift, args.shift
+ namespace = namespace.to_sym if namespace.respond_to?(:to_sym)
+ @options[:rails][method] = namespace
+ end
+
+ if configuration
+ aliases = configuration.delete(:aliases)
+ @aliases[namespace].merge!(aliases) if aliases
+ @options[namespace].merge!(configuration)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
new file mode 100644
index 0000000000..ac5836a588
--- /dev/null
+++ b/railties/lib/rails/console/app.rb
@@ -0,0 +1,37 @@
+require 'active_support/all'
+require 'action_controller'
+
+module Rails
+ module ConsoleMethods
+ # reference the global "app" instance, created on demand. To recreate the
+ # instance, pass a non-false value as the parameter.
+ def app(create=false)
+ @app_integration_instance = nil if create
+ @app_integration_instance ||= new_session do |sess|
+ sess.host! "www.example.com"
+ end
+ end
+
+ # create a new session. If a block is given, the new session will be yielded
+ # to the block before being returned.
+ def new_session
+ app = Rails.application
+ session = ActionDispatch::Integration::Session.new(app)
+ yield session if block_given?
+
+ # This makes app.url_for and app.foo_path available in the console
+ session.extend(app.routes.url_helpers)
+ session.extend(app.routes.mounted_helpers)
+
+ session
+ end
+
+ # reloads the environment
+ def reload!(print=true)
+ puts "Reloading..." if print
+ ActionDispatch::Reloader.cleanup!
+ ActionDispatch::Reloader.prepare!
+ true
+ end
+ end
+end
diff --git a/railties/lib/rails/console/helpers.rb b/railties/lib/rails/console/helpers.rb
new file mode 100644
index 0000000000..a33f71dc5b
--- /dev/null
+++ b/railties/lib/rails/console/helpers.rb
@@ -0,0 +1,17 @@
+module Rails
+ module ConsoleMethods
+ # Gets the helper methods available to the controller.
+ #
+ # This method assumes an +ApplicationController+ exists, and it extends +ActionController::Base+
+ def helper
+ ApplicationController.helpers
+ end
+
+ # Gets a new instance of a controller object.
+ #
+ # This method assumes an +ApplicationController+ exists, and it extends +ActionController::Base+
+ def controller
+ @controller ||= ApplicationController.new
+ end
+ end
+end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
new file mode 100644
index 0000000000..5757d235d2
--- /dev/null
+++ b/railties/lib/rails/engine.rb
@@ -0,0 +1,704 @@
+require 'rails/railtie'
+require 'rails/engine/railties'
+require 'active_support/core_ext/module/delegation'
+require 'pathname'
+require 'thread'
+
+module Rails
+ # <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
+ # functionality and share it with other applications or within a larger packaged application.
+ # Every <tt>Rails::Application</tt> is just an engine, which allows for simple
+ # feature and application sharing.
+ #
+ # Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
+ # methods (like <tt>rake_tasks</tt> and +generators+) and configuration
+ # options that are available in railties can also be used in engines.
+ #
+ # == Creating an Engine
+ #
+ # If you want a gem to behave as an engine, you have to specify an +Engine+
+ # for it somewhere inside your plugin's +lib+ folder (similar to how we
+ # specify a +Railtie+):
+ #
+ # # lib/my_engine.rb
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # end
+ # end
+ #
+ # Then ensure that this file is loaded at the top of your <tt>config/application.rb</tt>
+ # (or in your +Gemfile+) and it will automatically load models, controllers and helpers
+ # inside +app+, load routes at <tt>config/routes.rb</tt>, load locales at
+ # <tt>config/locales/*</tt>, and load tasks at <tt>lib/tasks/*</tt>.
+ #
+ # == Configuration
+ #
+ # Besides the +Railtie+ configuration which is shared across the application, in a
+ # <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt>, <tt>eager_load_paths</tt>
+ # and <tt>autoload_once_paths</tt>, which, differently from a <tt>Railtie</tt>, are scoped to
+ # the current engine.
+ #
+ # class MyEngine < Rails::Engine
+ # # Add a load path for this specific Engine
+ # config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
+ #
+ # initializer "my_engine.add_middleware" do |app|
+ # app.middleware.use MyEngine::Middleware
+ # end
+ # end
+ #
+ # == Generators
+ #
+ # You can set up generators for engines with <tt>config.generators</tt> method:
+ #
+ # class MyEngine < Rails::Engine
+ # config.generators do |g|
+ # g.orm :active_record
+ # g.template_engine :erb
+ # g.test_framework :test_unit
+ # end
+ # end
+ #
+ # You can also set generators for an application by using <tt>config.app_generators</tt>:
+ #
+ # class MyEngine < Rails::Engine
+ # # note that you can also pass block to app_generators in the same way you
+ # # can pass it to generators method
+ # config.app_generators.orm :datamapper
+ # end
+ #
+ # == Paths
+ #
+ # Applications and engines have flexible path configuration, meaning that you
+ # are not required to place your controllers at <tt>app/controllers</tt>, but
+ # in any place which you find convenient.
+ #
+ # For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
+ # You can set that as an option:
+ #
+ # class MyEngine < Rails::Engine
+ # paths["app/controllers"] = "lib/controllers"
+ # end
+ #
+ # You can also have your controllers loaded from both <tt>app/controllers</tt> and
+ # <tt>lib/controllers</tt>:
+ #
+ # class MyEngine < Rails::Engine
+ # paths["app/controllers"] << "lib/controllers"
+ # end
+ #
+ # The available paths in an engine are:
+ #
+ # class MyEngine < Rails::Engine
+ # paths["app"] # => ["app"]
+ # paths["app/controllers"] # => ["app/controllers"]
+ # paths["app/helpers"] # => ["app/helpers"]
+ # paths["app/models"] # => ["app/models"]
+ # paths["app/views"] # => ["app/views"]
+ # paths["lib"] # => ["lib"]
+ # paths["lib/tasks"] # => ["lib/tasks"]
+ # paths["config"] # => ["config"]
+ # paths["config/initializers"] # => ["config/initializers"]
+ # paths["config/locales"] # => ["config/locales"]
+ # paths["config/routes.rb"] # => ["config/routes.rb"]
+ # end
+ #
+ # The <tt>Application</tt> class adds a couple more paths to this set. And as in your
+ # <tt>Application</tt>, all folders under +app+ are automatically added to the load path.
+ # If you have an <tt>app/services</tt> folder for example, it will be added by default.
+ #
+ # == Endpoint
+ #
+ # An engine can also be a rack application. It can be useful if you have a rack application that
+ # you would like to wrap with +Engine+ and provide with some of the +Engine+'s features.
+ #
+ # To do that, use the +endpoint+ method:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # endpoint MyRackApplication
+ # end
+ # end
+ #
+ # Now you can mount your engine in application's routes just like that:
+ #
+ # Rails.application.routes.draw do
+ # mount MyEngine::Engine => "/engine"
+ # end
+ #
+ # == Middleware stack
+ #
+ # As an engine can now be a rack endpoint, it can also have a middleware
+ # stack. The usage is exactly the same as in <tt>Application</tt>:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # middleware.use SomeMiddleware
+ # end
+ # end
+ #
+ # == Routes
+ #
+ # If you don't specify an endpoint, routes will be used as the default
+ # endpoint. You can use them just like you use an application's routes:
+ #
+ # # ENGINE/config/routes.rb
+ # MyEngine::Engine.routes.draw do
+ # get "/" => "posts#index"
+ # end
+ #
+ # == Mount priority
+ #
+ # Note that now there can be more than one router in your application, and it's better to avoid
+ # passing requests through many routers. Consider this situation:
+ #
+ # Rails.application.routes.draw do
+ # mount MyEngine::Engine => "/blog"
+ # get "/blog/omg" => "main#omg"
+ # end
+ #
+ # +MyEngine+ is mounted at <tt>/blog</tt>, and <tt>/blog/omg</tt> points to application's
+ # controller. In such a situation, requests to <tt>/blog/omg</tt> will go through +MyEngine+,
+ # and if there is no such route in +Engine+'s routes, it will be dispatched to <tt>main#omg</tt>.
+ # It's much better to swap that:
+ #
+ # Rails.application.routes.draw do
+ # get "/blog/omg" => "main#omg"
+ # mount MyEngine::Engine => "/blog"
+ # end
+ #
+ # Now, +Engine+ will get only requests that were not handled by +Application+.
+ #
+ # == Engine name
+ #
+ # There are some places where an Engine's name is used:
+ #
+ # * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
+ # it's used as default <tt>:as</tt> option
+ # * rake task for installing migrations <tt>my_engine:install:migrations</tt>
+ #
+ # Engine name is set by default based on class name. For <tt>MyEngine::Engine</tt> it will be
+ # <tt>my_engine_engine</tt>. You can change it manually using the <tt>engine_name</tt> method:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # engine_name "my_engine"
+ # end
+ # end
+ #
+ # == Isolated Engine
+ #
+ # Normally when you create controllers, helpers and models inside an engine, they are treated
+ # as if they were created inside the application itself. This means that all helpers and
+ # named routes from the application will be available to your engine's controllers as well.
+ #
+ # However, sometimes you want to isolate your engine from the application, especially if your engine
+ # has its own router. To do that, you simply need to call +isolate_namespace+. This method requires
+ # you to pass a module where all your controllers, helpers and models should be nested to:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # isolate_namespace MyEngine
+ # end
+ # end
+ #
+ # With such an engine, everything that is inside the +MyEngine+ module will be isolated from
+ # the application.
+ #
+ # Consider this controller:
+ #
+ # module MyEngine
+ # class FooController < ActionController::Base
+ # end
+ # end
+ #
+ # If the +MyEngine+ engine is marked as isolated, +FooController+ only has
+ # access to helpers from +MyEngine+, and <tt>url_helpers</tt> from
+ # <tt>MyEngine::Engine.routes</tt>.
+ #
+ # The next thing that changes in isolated engines is the behavior of routes.
+ # Normally, when you namespace your controllers, you also need to namespace
+ # the related routes. With an isolated engine, the engine's namespace is
+ # automatically applied, so you don't need to specify it explicity in your
+ # routes:
+ #
+ # MyEngine::Engine.routes.draw do
+ # resources :articles
+ # end
+ #
+ # If +MyEngine+ is isolated, The routes above will point to
+ # <tt>MyEngine::ArticlesController</tt>. You also don't need to use longer
+ # url helpers like +my_engine_articles_path+. Instead, you should simply use
+ # +articles_path+, like you would do with your main application.
+ #
+ # To make this behavior consistent with other parts of the framework,
+ # isolated engines also have an effect on <tt>ActiveModel::Naming</tt>. In a
+ # normal Rails app, when you use a namespaced model such as
+ # <tt>Namespace::Article</tt>, <tt>ActiveModel::Naming</tt> will generate
+ # names with the prefix "namespace". In an isolated engine, the prefix will
+ # be omitted in url helpers and form fields, for convenience.
+ #
+ # polymorphic_url(MyEngine::Article.new)
+ # # => "articles_path" # not "my_engine_articles_path"
+ #
+ # form_for(MyEngine::Article.new) do
+ # text_field :title # => <input type="text" name="article[title]" id="article_title" />
+ # end
+ #
+ # Additionally, an isolated engine will set its own name according to its
+ # namespace, so <tt>MyEngine::Engine.engine_name</tt> will return
+ # "my_engine". It will also set +MyEngine.table_name_prefix+ to "my_engine_",
+ # meaning for example that <tt>MyEngine::Article</tt> will use the
+ # +my_engine_articles+ database table by default.
+ #
+ # == Using Engine's routes outside Engine
+ #
+ # Since you can now mount an engine inside application's routes, you do not have direct access to +Engine+'s
+ # <tt>url_helpers</tt> inside +Application+. When you mount an engine in an application's routes, a special helper is
+ # created to allow you to do that. Consider such a scenario:
+ #
+ # # config/routes.rb
+ # Rails.application.routes.draw do
+ # mount MyEngine::Engine => "/my_engine", as: "my_engine"
+ # get "/foo" => "foo#index"
+ # end
+ #
+ # Now, you can use the <tt>my_engine</tt> helper inside your application:
+ #
+ # class FooController < ApplicationController
+ # def index
+ # my_engine.root_url # => /my_engine/
+ # end
+ # end
+ #
+ # There is also a <tt>main_app</tt> helper that gives you access to application's routes inside Engine:
+ #
+ # module MyEngine
+ # class BarController
+ # def index
+ # main_app.foo_path # => /foo
+ # end
+ # end
+ # end
+ #
+ # Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tt> as default, so most of the time
+ # you can simply omit it.
+ #
+ # Finally, if you want to generate a url to an engine's route using
+ # <tt>polymorphic_url</tt>, you also need to pass the engine helper. Let's
+ # say that you want to create a form pointing to one of the engine's routes.
+ # All you need to do is pass the helper as the first element in array with
+ # attributes for url:
+ #
+ # form_for([my_engine, @user])
+ #
+ # This code will use <tt>my_engine.user_path(@user)</tt> to generate the proper route.
+ #
+ # == Isolated engine's helpers
+ #
+ # Sometimes you may want to isolate engine, but use helpers that are defined for it.
+ # If you want to share just a few specific helpers you can add them to application's
+ # helpers in ApplicationController:
+ #
+ # class ApplicationController < ActionController::Base
+ # helper MyEngine::SharedEngineHelper
+ # end
+ #
+ # If you want to include all of the engine's helpers, you can use the #helper method on an engine's
+ # instance:
+ #
+ # class ApplicationController < ActionController::Base
+ # helper MyEngine::Engine.helpers
+ # end
+ #
+ # It will include all of the helpers from engine's directory. Take into account that this does
+ # not include helpers defined in controllers with helper_method or other similar solutions,
+ # only helpers defined in the helpers directory will be included.
+ #
+ # == Migrations & seed data
+ #
+ # Engines can have their own migrations. The default path for migrations is exactly the same
+ # as in application: <tt>db/migrate</tt>
+ #
+ # To use engine's migrations in application you can use the rake task below, which copies them to
+ # application's dir:
+ #
+ # rake ENGINE_NAME:install:migrations
+ #
+ # Note that some of the migrations may be skipped if a migration with the same name already exists
+ # in application. In such a situation you must decide whether to leave that migration or rename the
+ # migration in the application and rerun copying migrations.
+ #
+ # If your engine has migrations, you may also want to prepare data for the database in
+ # the <tt>db/seeds.rb</tt> file. You can load that data using the <tt>load_seed</tt> method, e.g.
+ #
+ # MyEngine::Engine.load_seed
+ #
+ # == Loading priority
+ #
+ # In order to change engine's priority you can use +config.railties_order+ in the main application.
+ # It will affect the priority of loading views, helpers, assets and all the other files
+ # related to engine or application.
+ #
+ # # load Blog::Engine with highest priority, followed by application and other railties
+ # config.railties_order = [Blog::Engine, :main_app, :all]
+ class Engine < Railtie
+ autoload :Configuration, "rails/engine/configuration"
+
+ class << self
+ attr_accessor :called_from, :isolated
+
+ alias :isolated? :isolated
+ alias :engine_name :railtie_name
+
+ delegate :eager_load!, to: :instance
+
+ def inherited(base)
+ unless base.abstract_railtie?
+ Rails::Railtie::Configuration.eager_load_namespaces << base
+
+ base.called_from = begin
+ call_stack = caller_locations.map { |l| l.absolute_path || l.path }
+
+ File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] })
+ end
+ end
+
+ super
+ end
+
+ def find_root(from)
+ find_root_with_flag "lib", from
+ end
+
+ def endpoint(endpoint = nil)
+ @endpoint ||= nil
+ @endpoint = endpoint if endpoint
+ @endpoint
+ end
+
+ def isolate_namespace(mod)
+ engine_name(generate_railtie_name(mod.name))
+
+ self.routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) }
+ self.isolated = true
+
+ unless mod.respond_to?(:railtie_namespace)
+ name, railtie = engine_name, self
+
+ mod.singleton_class.instance_eval do
+ define_method(:railtie_namespace) { railtie }
+
+ unless mod.respond_to?(:table_name_prefix)
+ define_method(:table_name_prefix) { "#{name}_" }
+ end
+
+ unless mod.respond_to?(:use_relative_model_naming?)
+ class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__
+ end
+
+ unless mod.respond_to?(:railtie_helpers_paths)
+ define_method(:railtie_helpers_paths) { railtie.helpers_paths }
+ end
+
+ unless mod.respond_to?(:railtie_routes_url_helpers)
+ define_method(:railtie_routes_url_helpers) {|include_path_helpers = true| railtie.routes.url_helpers(include_path_helpers) }
+ end
+ end
+ end
+ end
+
+ # Finds engine with given path.
+ def find(path)
+ expanded_path = File.expand_path path
+ Rails::Engine.subclasses.each do |klass|
+ engine = klass.instance
+ return engine if File.expand_path(engine.root) == expanded_path
+ end
+ nil
+ end
+ end
+
+ delegate :middleware, :root, :paths, to: :config
+ delegate :engine_name, :isolated?, to: :class
+
+ def initialize
+ @_all_autoload_paths = nil
+ @_all_load_paths = nil
+ @app = nil
+ @config = nil
+ @env_config = nil
+ @helpers = nil
+ @routes = nil
+ @app_build_lock = Mutex.new
+ super
+ end
+
+ # Load console and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.console</tt> for more info.
+ def load_console(app=self)
+ require "rails/console/app"
+ require "rails/console/helpers"
+ run_console_blocks(app)
+ self
+ end
+
+ # Load Rails runner and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.runner</tt> for more info.
+ def load_runner(app=self)
+ run_runner_blocks(app)
+ self
+ end
+
+ # Load Rake, railties tasks and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
+ def load_tasks(app=self)
+ require "rake"
+ run_tasks_blocks(app)
+ self
+ end
+
+ # Load Rails generators and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.generators</tt> for more info.
+ def load_generators(app=self)
+ require "rails/generators"
+ run_generators_blocks(app)
+ Rails::Generators.configure!(app.config.generators)
+ self
+ end
+
+ # Eager load the application by loading all ruby
+ # files inside eager_load paths.
+ def eager_load!
+ config.eager_load_paths.each do |load_path|
+ matcher = /\A#{Regexp.escape(load_path.to_s)}\/(.*)\.rb\Z/
+ Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
+ require_dependency file.sub(matcher, '\1')
+ end
+ end
+ end
+
+ def railties
+ @railties ||= Railties.new
+ end
+
+ # Returns a module with all the helpers defined for the engine.
+ def helpers
+ @helpers ||= begin
+ helpers = Module.new
+ all = ActionController::Base.all_helpers_from_path(helpers_paths)
+ ActionController::Base.modules_for_helpers(all).each do |mod|
+ helpers.include(mod)
+ end
+ helpers
+ end
+ end
+
+ # Returns all registered helpers paths.
+ def helpers_paths
+ paths["app/helpers"].existent
+ end
+
+ # Returns the underlying rack application for this engine.
+ def app
+ @app || @app_build_lock.synchronize {
+ @app ||= begin
+ stack = default_middleware_stack
+ config.middleware = build_middleware.merge_into(stack)
+ config.middleware.build(endpoint)
+ end
+ }
+ end
+
+ # Returns the endpoint for this engine. If none is registered,
+ # defaults to an ActionDispatch::Routing::RouteSet.
+ def endpoint
+ self.class.endpoint || routes
+ end
+
+ # Define the Rack API for this engine.
+ def call(env)
+ req = build_request env
+ app.call req.env
+ end
+
+ # Defines additional Rack env configuration that is added on each call.
+ def env_config
+ @env_config ||= {}
+ end
+
+ # Defines the routes for this engine. If a block is given to
+ # routes, it is appended to the engine.
+ def routes
+ @routes ||= ActionDispatch::Routing::RouteSet.new_with_config(config)
+ @routes.append(&Proc.new) if block_given?
+ @routes
+ end
+
+ # Define the configuration object for the engine.
+ def config
+ @config ||= Engine::Configuration.new(self.class.find_root(self.class.called_from))
+ end
+
+ # Load data from db/seeds.rb file. It can be used in to load engines'
+ # seeds, e.g.:
+ #
+ # Blog::Engine.load_seed
+ def load_seed
+ seed_file = paths["db/seeds.rb"].existent.first
+ load(seed_file) if seed_file
+ end
+
+ # Add configured load paths to ruby load paths and remove duplicates.
+ initializer :set_load_path, before: :bootstrap_hook do
+ _all_load_paths.reverse_each do |path|
+ $LOAD_PATH.unshift(path) if File.directory?(path)
+ end
+ $LOAD_PATH.uniq!
+ end
+
+ # Set the paths from which Rails will automatically load source files,
+ # and the load_once paths.
+ #
+ # This needs to be an initializer, since it needs to run once
+ # per engine and get the engine as a block parameter.
+ initializer :set_autoload_paths, before: :bootstrap_hook do
+ ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
+ ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
+
+ # Freeze so future modifications will fail rather than do nothing mysteriously
+ config.autoload_paths.freeze
+ config.eager_load_paths.freeze
+ config.autoload_once_paths.freeze
+ end
+
+ initializer :add_routing_paths do |app|
+ routing_paths = self.paths["config/routes.rb"].existent
+
+ if routes? || routing_paths.any?
+ app.routes_reloader.paths.unshift(*routing_paths)
+ app.routes_reloader.route_sets << routes
+ end
+ end
+
+ # I18n load paths are a special case since the ones added
+ # later have higher priority.
+ initializer :add_locales do
+ config.i18n.railties_load_path << paths["config/locales"]
+ end
+
+ initializer :add_view_paths do
+ views = paths["app/views"].existent
+ unless views.empty?
+ ActiveSupport.on_load(:action_controller){ prepend_view_path(views) if respond_to?(:prepend_view_path) }
+ ActiveSupport.on_load(:action_mailer){ prepend_view_path(views) }
+ end
+ end
+
+ initializer :load_environment_config, before: :load_environment_hook, group: :all do
+ paths["config/environments"].existent.each do |environment|
+ require environment
+ end
+ end
+
+ initializer :prepend_helpers_path do |app|
+ if !isolated? || (app == self)
+ app.config.helpers_paths.unshift(*paths["app/helpers"].existent)
+ end
+ end
+
+ initializer :load_config_initializers do
+ config.paths["config/initializers"].existent.sort.each do |initializer|
+ load_config_initializer(initializer)
+ end
+ end
+
+ initializer :engines_blank_point do
+ # We need this initializer so all extra initializers added in engines are
+ # consistently executed after all the initializers above across all engines.
+ end
+
+ rake_tasks do
+ next if self.is_a?(Rails::Application)
+ next unless has_migrations?
+
+ namespace railtie_name do
+ namespace :install do
+ desc "Copy migrations from #{railtie_name} to application"
+ task :migrations do
+ ENV["FROM"] = railtie_name
+ if Rake::Task.task_defined?("railties:install:migrations")
+ Rake::Task["railties:install:migrations"].invoke
+ else
+ Rake::Task["app:railties:install:migrations"].invoke
+ end
+ end
+ end
+ end
+ end
+
+ def routes? #:nodoc:
+ @routes
+ end
+
+ protected
+
+ def load_config_initializer(initializer)
+ ActiveSupport::Notifications.instrument('load_config_initializer.railties', initializer: initializer) do
+ load(initializer)
+ end
+ end
+
+ def run_tasks_blocks(*) #:nodoc:
+ super
+ paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
+ end
+
+ def has_migrations? #:nodoc:
+ paths["db/migrate"].existent.any?
+ end
+
+ def self.find_root_with_flag(flag, root_path, default=nil) #:nodoc:
+
+ while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
+ parent = File.dirname(root_path)
+ root_path = parent != root_path && parent
+ end
+
+ root = File.exist?("#{root_path}/#{flag}") ? root_path : default
+ raise "Could not find root path for #{self}" unless root
+
+ Pathname.new File.realpath root
+ end
+
+ def default_middleware_stack #:nodoc:
+ ActionDispatch::MiddlewareStack.new
+ end
+
+ def _all_autoload_once_paths #:nodoc:
+ config.autoload_once_paths
+ end
+
+ def _all_autoload_paths #:nodoc:
+ @_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
+ end
+
+ def _all_load_paths #:nodoc:
+ @_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
+ end
+
+ private
+
+ def build_request(env)
+ env.merge!(env_config)
+ req = ActionDispatch::Request.new env
+ req.routes = routes
+ req.engine_script_name = req.script_name
+ req
+ end
+
+ def build_middleware
+ config.middleware
+ end
+ end
+end
diff --git a/railties/lib/rails/engine/commands.rb b/railties/lib/rails/engine/commands.rb
new file mode 100644
index 0000000000..a6d87b78e4
--- /dev/null
+++ b/railties/lib/rails/engine/commands.rb
@@ -0,0 +1,43 @@
+ARGV << '--help' if ARGV.empty?
+
+aliases = {
+ "g" => "generate",
+ "d" => "destroy",
+ "t" => "test"
+}
+
+command = ARGV.shift
+command = aliases[command] || command
+
+require ENGINE_PATH
+engine = ::Rails::Engine.find(ENGINE_ROOT)
+
+case command
+when 'generate', 'destroy', 'test'
+ require 'rails/generators'
+ Rails::Generators.namespace = engine.railtie_namespace
+ engine.load_generators
+ require "rails/commands/#{command}"
+
+when '--version', '-v'
+ ARGV.unshift '--version'
+ require 'rails/commands/application'
+
+else
+ puts "Error: Command not recognized" unless %w(-h --help).include?(command)
+ puts <<-EOT
+Usage: rails COMMAND [ARGS]
+
+The common Rails commands available for engines are:
+ generate Generate new code (short-cut alias: "g")
+ destroy Undo code generated with "generate" (short-cut alias: "d")
+ test Run tests (short-cut alias: "t")
+
+All commands can be run with -h for more information.
+
+If you want to run any commands that need to be run in context
+of the application, like `rails server` or `rails console`,
+you should do it from application's directory (typically test/dummy).
+ EOT
+ exit(1)
+end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
new file mode 100644
index 0000000000..8cadbc3ddd
--- /dev/null
+++ b/railties/lib/rails/engine/configuration.rb
@@ -0,0 +1,85 @@
+require 'rails/railtie/configuration'
+
+module Rails
+ class Engine
+ class Configuration < ::Rails::Railtie::Configuration
+ attr_reader :root
+ attr_accessor :middleware
+ attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths
+
+ def initialize(root=nil)
+ super()
+ @root = root
+ @generators = app_generators.dup
+ @middleware = Rails::Configuration::MiddlewareStackProxy.new
+ end
+
+ # Holds generators configuration:
+ #
+ # config.generators do |g|
+ # g.orm :data_mapper, migration: true
+ # g.template_engine :haml
+ # g.test_framework :rspec
+ # end
+ #
+ # If you want to disable color in console, do:
+ #
+ # config.generators.colorize_logging = false
+ #
+ def generators
+ @generators ||= Rails::Configuration::Generators.new
+ yield(@generators) if block_given?
+ @generators
+ end
+
+ def paths
+ @paths ||= begin
+ paths = Rails::Paths::Root.new(@root)
+
+ paths.add "app", eager_load: true, glob: "{*,*/concerns}"
+ paths.add "app/assets", glob: "*"
+ paths.add "app/controllers", eager_load: true
+ paths.add "app/helpers", eager_load: true
+ paths.add "app/models", eager_load: true
+ paths.add "app/mailers", eager_load: true
+ paths.add "app/views"
+
+ paths.add "lib", load_path: true
+ paths.add "lib/assets", glob: "*"
+ paths.add "lib/tasks", glob: "**/*.rake"
+
+ paths.add "config"
+ paths.add "config/environments", glob: "#{Rails.env}.rb"
+ paths.add "config/initializers", glob: "**/*.rb"
+ paths.add "config/locales", glob: "*.{rb,yml}"
+ paths.add "config/routes.rb"
+
+ paths.add "db"
+ paths.add "db/migrate"
+ paths.add "db/seeds.rb"
+
+ paths.add "vendor", load_path: true
+ paths.add "vendor/assets", glob: "*"
+
+ paths
+ end
+ end
+
+ def root=(value)
+ @root = paths.path = Pathname.new(value).expand_path
+ end
+
+ def eager_load_paths
+ @eager_load_paths ||= paths.eager_load
+ end
+
+ def autoload_once_paths
+ @autoload_once_paths ||= paths.autoload_once
+ end
+
+ def autoload_paths
+ @autoload_paths ||= paths.autoload_paths
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
new file mode 100644
index 0000000000..9969a1475d
--- /dev/null
+++ b/railties/lib/rails/engine/railties.rb
@@ -0,0 +1,21 @@
+module Rails
+ class Engine < Railtie
+ class Railties
+ include Enumerable
+ attr_reader :_all
+
+ def initialize
+ @_all ||= ::Rails::Railtie.subclasses.map(&:instance) +
+ ::Rails::Engine.subclasses.map(&:instance)
+ end
+
+ def each(*args, &block)
+ _all.each(*args, &block)
+ end
+
+ def -(others)
+ _all - others
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb
new file mode 100644
index 0000000000..7d74b1bfe5
--- /dev/null
+++ b/railties/lib/rails/gem_version.rb
@@ -0,0 +1,15 @@
+module Rails
+ # Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
+ def self.gem_version
+ Gem::Version.new VERSION::STRING
+ end
+
+ module VERSION
+ MAJOR = 5
+ MINOR = 0
+ TINY = 0
+ PRE = "alpha"
+
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
+ end
+end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
new file mode 100644
index 0000000000..e3d79521e7
--- /dev/null
+++ b/railties/lib/rails/generators.rb
@@ -0,0 +1,394 @@
+activesupport_path = File.expand_path('../../../../activesupport/lib', __FILE__)
+$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
+
+require 'thor/group'
+
+require 'active_support'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/hash/deep_merge'
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/string/inflections'
+
+module Rails
+ module Generators
+ autoload :Actions, 'rails/generators/actions'
+ autoload :ActiveModel, 'rails/generators/active_model'
+ autoload :Base, 'rails/generators/base'
+ autoload :Migration, 'rails/generators/migration'
+ autoload :NamedBase, 'rails/generators/named_base'
+ autoload :ResourceHelpers, 'rails/generators/resource_helpers'
+ autoload :TestCase, 'rails/generators/test_case'
+
+ mattr_accessor :namespace
+
+ DEFAULT_ALIASES = {
+ rails: {
+ actions: '-a',
+ orm: '-o',
+ javascripts: '-j',
+ javascript_engine: '-je',
+ resource_controller: '-c',
+ scaffold_controller: '-c',
+ stylesheets: '-y',
+ stylesheet_engine: '-se',
+ scaffold_stylesheet: '-ss',
+ template_engine: '-e',
+ test_framework: '-t'
+ },
+
+ test_unit: {
+ fixture_replacement: '-r',
+ }
+ }
+
+ DEFAULT_OPTIONS = {
+ rails: {
+ api: false,
+ assets: true,
+ force_plural: false,
+ helper: true,
+ integration_tool: nil,
+ javascripts: true,
+ javascript_engine: :js,
+ orm: false,
+ resource_controller: :controller,
+ resource_route: true,
+ scaffold_controller: :scaffold_controller,
+ stylesheets: true,
+ stylesheet_engine: :css,
+ scaffold_stylesheet: true,
+ test_framework: false,
+ template_engine: :erb
+ }
+ }
+
+ def self.configure!(config) #:nodoc:
+ api_only! if config.api_only
+ no_color! unless config.colorize_logging
+ aliases.deep_merge! config.aliases
+ options.deep_merge! config.options
+ fallbacks.merge! config.fallbacks
+ templates_path.concat config.templates
+ templates_path.uniq!
+ hide_namespaces(*config.hidden_namespaces)
+ end
+
+ def self.templates_path #:nodoc:
+ @templates_path ||= []
+ end
+
+ def self.aliases #:nodoc:
+ @aliases ||= DEFAULT_ALIASES.dup
+ end
+
+ def self.options #:nodoc:
+ @options ||= DEFAULT_OPTIONS.dup
+ end
+
+ # Hold configured generators fallbacks. If a plugin developer wants a
+ # generator group to fallback to another group in case of missing generators,
+ # they can add a fallback.
+ #
+ # For example, shoulda is considered a test_framework and is an extension
+ # of test_unit. However, most part of shoulda generators are similar to
+ # test_unit ones.
+ #
+ # Shoulda then can tell generators to search for test_unit generators when
+ # some of them are not available by adding a fallback:
+ #
+ # Rails::Generators.fallbacks[:shoulda] = :test_unit
+ def self.fallbacks
+ @fallbacks ||= {}
+ end
+
+ # Configure generators for API only applications. It basically hides
+ # everything that is usually browser related, such as assets and session
+ # migration generators, and completely disable views, helpers and assets
+ # so generators such as scaffold won't create them.
+ def self.api_only!
+ hide_namespaces "assets", "helper", "css", "js"
+
+ options[:rails].merge!(
+ api: true,
+ assets: false,
+ helper: false,
+ template_engine: nil
+ )
+ end
+
+ # Remove the color from output.
+ def self.no_color!
+ Thor::Base.shell = Thor::Shell::Basic
+ end
+
+ # Track all generators subclasses.
+ def self.subclasses
+ @subclasses ||= []
+ end
+
+ # Rails finds namespaces similar to thor, it only adds one rule:
+ #
+ # Generators names must end with "_generator.rb". This is required because Rails
+ # looks in load paths and loads the generator just before it's going to be used.
+ #
+ # find_by_namespace :webrat, :rails, :integration
+ #
+ # Will search for the following generators:
+ #
+ # "rails:webrat", "webrat:integration", "webrat"
+ #
+ # Notice that "rails:generators:webrat" could be loaded as well, what
+ # Rails looks for is the first and last parts of the namespace.
+ def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
+ lookups = []
+ lookups << "#{base}:#{name}" if base
+ lookups << "#{name}:#{context}" if context
+
+ unless base || context
+ unless name.to_s.include?(?:)
+ lookups << "#{name}:#{name}"
+ lookups << "rails:#{name}"
+ end
+ lookups << "#{name}"
+ end
+
+ lookup(lookups)
+
+ namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
+
+ lookups.each do |namespace|
+ klass = namespaces[namespace]
+ return klass if klass
+ end
+
+ invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
+ end
+
+ # Receives a namespace, arguments and the behavior to invoke the generator.
+ # It's used as the default entry point for generate, destroy and update
+ # commands.
+ def self.invoke(namespace, args=ARGV, config={})
+ names = namespace.to_s.split(':')
+ if klass = find_by_namespace(names.pop, names.any? && names.join(':'))
+ args << "--help" if args.empty? && klass.arguments.any?(&:required?)
+ klass.start(args, config)
+ else
+ options = sorted_groups.flat_map(&:last)
+ suggestions = options.sort_by {|suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
+ msg = "Could not find generator '#{namespace}'. "
+ msg << "Maybe you meant #{ suggestions.map {|s| "'#{s}'"}.to_sentence(last_word_connector: " or ", locale: :en) }\n"
+ msg << "Run `rails generate --help` for more options."
+ puts msg
+ end
+ end
+
+ # Returns an array of generator namespaces that are hidden.
+ # Generator namespaces may be hidden for a variety of reasons.
+ # Some are aliased such as "rails:migration" and can be
+ # invoked with the shorter "migration", others are private to other generators
+ # such as "css:scaffold".
+ def self.hidden_namespaces
+ @hidden_namespaces ||= begin
+ orm = options[:rails][:orm]
+ test = options[:rails][:test_framework]
+ template = options[:rails][:template_engine]
+ css = options[:rails][:stylesheet_engine]
+
+ [
+ "rails",
+ "resource_route",
+ "#{orm}:migration",
+ "#{orm}:model",
+ "#{test}:controller",
+ "#{test}:helper",
+ "#{test}:integration",
+ "#{test}:mailer",
+ "#{test}:model",
+ "#{test}:scaffold",
+ "#{test}:view",
+ "#{test}:job",
+ "#{template}:controller",
+ "#{template}:scaffold",
+ "#{template}:mailer",
+ "#{css}:scaffold",
+ "#{css}:assets",
+ "css:assets",
+ "css:scaffold"
+ ]
+ end
+ end
+
+ class << self
+ def hide_namespaces(*namespaces)
+ hidden_namespaces.concat(namespaces)
+ end
+ alias hide_namespace hide_namespaces
+ end
+
+ # Show help message with available generators.
+ def self.help(command = 'generate')
+ puts "Usage: rails #{command} GENERATOR [args] [options]"
+ puts
+ puts "General options:"
+ puts " -h, [--help] # Print generator's options and usage"
+ puts " -p, [--pretend] # Run but do not make any changes"
+ puts " -f, [--force] # Overwrite files that already exist"
+ puts " -s, [--skip] # Skip files that already exist"
+ puts " -q, [--quiet] # Suppress status output"
+ puts
+ puts "Please choose a generator below."
+ puts
+
+ print_generators
+ end
+
+ def self.public_namespaces
+ lookup!
+ subclasses.map(&:namespace)
+ end
+
+ def self.print_generators
+ sorted_groups.each { |b, n| print_list(b, n) }
+ end
+
+ def self.sorted_groups
+ namespaces = public_namespaces
+ namespaces.sort!
+ groups = Hash.new { |h,k| h[k] = [] }
+ namespaces.each do |namespace|
+ base = namespace.split(':').first
+ groups[base] << namespace
+ end
+ rails = groups.delete("rails")
+ rails.map! { |n| n.sub(/^rails:/, '') }
+ rails.delete("app")
+ rails.delete("plugin")
+
+ hidden_namespaces.each { |n| groups.delete(n.to_s) }
+
+ [["rails", rails]] + groups.sort.to_a
+ end
+
+ protected
+
+ # This code is based directly on the Text gem implementation
+ # Returns a value representing the "cost" of transforming str1 into str2
+ def self.levenshtein_distance str1, str2
+ s = str1
+ t = str2
+ n = s.length
+ m = t.length
+
+ return m if (0 == n)
+ return n if (0 == m)
+
+ d = (0..m).to_a
+ x = nil
+
+ # avoid duplicating an enumerable object in the loop
+ str2_codepoint_enumerable = str2.each_codepoint
+
+ str1.each_codepoint.with_index do |char1, i|
+ e = i+1
+
+ str2_codepoint_enumerable.with_index do |char2, j|
+ cost = (char1 == char2) ? 0 : 1
+ x = [
+ d[j+1] + 1, # insertion
+ e + 1, # deletion
+ d[j] + cost # substitution
+ ].min
+ d[j] = e
+ e = x
+ end
+
+ d[m] = x
+ end
+
+ x
+ end
+
+ # Prints a list of generators.
+ def self.print_list(base, namespaces) #:nodoc:
+ namespaces = namespaces.reject do |n|
+ hidden_namespaces.include?(n)
+ end
+
+ return if namespaces.empty?
+ puts "#{base.camelize}:"
+
+ namespaces.each do |namespace|
+ puts(" #{namespace}")
+ end
+
+ puts
+ end
+
+ # Try fallbacks for the given base.
+ def self.invoke_fallbacks_for(name, base) #:nodoc:
+ return nil unless base && fallbacks[base.to_sym]
+ invoked_fallbacks = []
+
+ Array(fallbacks[base.to_sym]).each do |fallback|
+ next if invoked_fallbacks.include?(fallback)
+ invoked_fallbacks << fallback
+
+ klass = find_by_namespace(name, fallback)
+ return klass if klass
+ end
+
+ nil
+ end
+
+ # Receives namespaces in an array and tries to find matching generators
+ # in the load path.
+ def self.lookup(namespaces) #:nodoc:
+ paths = namespaces_to_paths(namespaces)
+
+ paths.each do |raw_path|
+ ["rails/generators", "generators"].each do |base|
+ path = "#{base}/#{raw_path}_generator"
+
+ begin
+ require path
+ return
+ rescue LoadError => e
+ raise unless e.message =~ /#{Regexp.escape(path)}$/
+ rescue Exception => e
+ warn "[WARNING] Could not load generator #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
+ end
+ end
+ end
+ end
+
+ # This will try to load any generator in the load path to show in help.
+ def self.lookup! #:nodoc:
+ $LOAD_PATH.each do |base|
+ Dir[File.join(base, "{rails/generators,generators}", "**", "*_generator.rb")].each do |path|
+ begin
+ path = path.sub("#{base}/", "")
+ require path
+ rescue Exception
+ # No problem
+ end
+ end
+ end
+ end
+
+ # Convert namespaces to paths by replacing ":" for "/" and adding
+ # an extra lookup. For example, "rails:model" should be searched
+ # in both: "rails/model/model_generator" and "rails/model_generator".
+ def self.namespaces_to_paths(namespaces) #:nodoc:
+ paths = []
+ namespaces.each do |namespace|
+ pieces = namespace.split(":")
+ paths << pieces.dup.push(pieces.last).join("/")
+ paths << pieces.join("/")
+ end
+ paths.uniq!
+ paths
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
new file mode 100644
index 0000000000..5bbd2f1aed
--- /dev/null
+++ b/railties/lib/rails/generators/actions.rb
@@ -0,0 +1,295 @@
+require 'open-uri'
+
+module Rails
+ module Generators
+ module Actions
+ def initialize(*) # :nodoc:
+ super
+ @in_group = nil
+ @after_bundle_callbacks = []
+ end
+
+ # Adds an entry into +Gemfile+ for the supplied gem.
+ #
+ # gem "rspec", group: :test
+ # gem "technoweenie-restful-authentication", lib: "restful-authentication", source: "http://gems.github.com/"
+ # gem "rails", "3.0", git: "git://github.com/rails/rails"
+ def gem(*args)
+ options = args.extract_options!
+ name, version = args
+
+ # Set the message to be shown in logs. Uses the git repo if one is given,
+ # otherwise use name (version).
+ parts, message = [ quote(name) ], name
+ if version ||= options.delete(:version)
+ parts << quote(version)
+ message << " (#{version})"
+ end
+ message = options[:git] if options[:git]
+
+ log :gemfile, message
+
+ options.each do |option, value|
+ parts << "#{option}: #{quote(value)}"
+ end
+
+ in_root do
+ str = "gem #{parts.join(", ")}"
+ str = " " + str if @in_group
+ str = "\n" + str
+ append_file "Gemfile", str, verbose: false
+ end
+ end
+
+ # Wraps gem entries inside a group.
+ #
+ # gem_group :development, :test do
+ # gem "rspec-rails"
+ # end
+ def gem_group(*names, &block)
+ name = names.map(&:inspect).join(", ")
+ log :gemfile, "group #{name}"
+
+ in_root do
+ append_file "Gemfile", "\ngroup #{name} do", force: true
+
+ @in_group = true
+ instance_eval(&block)
+ @in_group = false
+
+ append_file "Gemfile", "\nend\n", force: true
+ end
+ end
+
+ # Add the given source to +Gemfile+
+ #
+ # If block is given, gem entries in block are wrapped into the source group.
+ #
+ # add_source "http://gems.github.com/"
+ #
+ # add_source "http://gems.github.com/" do
+ # gem "rspec-rails"
+ # end
+ def add_source(source, options={}, &block)
+ log :source, source
+
+ in_root do
+ if block
+ append_file "Gemfile", "source #{quote(source)} do", force: true
+ @in_group = true
+ instance_eval(&block)
+ @in_group = false
+ append_file "Gemfile", "\nend\n", force: true
+ else
+ prepend_file "Gemfile", "source #{quote(source)}\n", verbose: false
+ end
+ end
+ end
+
+ # Adds a line inside the Application class for <tt>config/application.rb</tt>.
+ #
+ # If options <tt>:env</tt> is specified, the line is appended to the corresponding
+ # file in <tt>config/environments</tt>.
+ #
+ # environment do
+ # "config.action_controller.asset_host = 'cdn.provider.com'"
+ # end
+ #
+ # environment(nil, env: "development") do
+ # "config.action_controller.asset_host = 'localhost:3000'"
+ # end
+ def environment(data=nil, options={})
+ sentinel = /class [a-z_:]+ < Rails::Application/i
+ env_file_sentinel = /Rails\.application\.configure do/
+ data = yield if !data && block_given?
+
+ in_root do
+ if options[:env].nil?
+ inject_into_file 'config/application.rb', "\n #{data}", after: sentinel, verbose: false
+ else
+ Array(options[:env]).each do |env|
+ inject_into_file "config/environments/#{env}.rb", "\n #{data}", after: env_file_sentinel, verbose: false
+ end
+ end
+ end
+ end
+ alias :application :environment
+
+ # Run a command in git.
+ #
+ # git :init
+ # git add: "this.file that.rb"
+ # git add: "onefile.rb", rm: "badfile.cxx"
+ def git(commands={})
+ if commands.is_a?(Symbol)
+ run "git #{commands}"
+ else
+ commands.each do |cmd, options|
+ run "git #{cmd} #{options}"
+ end
+ end
+ end
+
+ # Create a new file in the <tt>vendor/</tt> directory. Code can be specified
+ # in a block or a data string can be given.
+ #
+ # vendor("sekrit.rb") do
+ # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--"
+ # "salt = '#{sekrit_salt}'"
+ # end
+ #
+ # vendor("foreign.rb", "# Foreign code is fun")
+ def vendor(filename, data=nil, &block)
+ log :vendor, filename
+ create_file("vendor/#{filename}", data, verbose: false, &block)
+ end
+
+ # Create a new file in the lib/ directory. Code can be specified
+ # in a block or a data string can be given.
+ #
+ # lib("crypto.rb") do
+ # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
+ # end
+ #
+ # lib("foreign.rb", "# Foreign code is fun")
+ def lib(filename, data=nil, &block)
+ log :lib, filename
+ create_file("lib/#{filename}", data, verbose: false, &block)
+ end
+
+ # Create a new +Rakefile+ with the provided code (either in a block or a string).
+ #
+ # rakefile("bootstrap.rake") do
+ # project = ask("What is the UNIX name of your project?")
+ #
+ # <<-TASK
+ # namespace :#{project} do
+ # task :bootstrap do
+ # puts "I like boots!"
+ # end
+ # end
+ # TASK
+ # end
+ #
+ # rakefile('seed.rake', 'puts "Planting seeds"')
+ def rakefile(filename, data=nil, &block)
+ log :rakefile, filename
+ create_file("lib/tasks/#{filename}", data, verbose: false, &block)
+ end
+
+ # Create a new initializer with the provided code (either in a block or a string).
+ #
+ # initializer("globals.rb") do
+ # data = ""
+ #
+ # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do |const|
+ # data << "#{const} = :entp\n"
+ # end
+ #
+ # data
+ # end
+ #
+ # initializer("api.rb", "API_KEY = '123456'")
+ def initializer(filename, data=nil, &block)
+ log :initializer, filename
+ create_file("config/initializers/#{filename}", data, verbose: false, &block)
+ end
+
+ # Generate something using a generator from Rails or a plugin.
+ # The second parameter is the argument string that is passed to
+ # the generator or an Array that is joined.
+ #
+ # generate(:authenticated, "user session")
+ def generate(what, *args)
+ log :generate, what
+ argument = args.flat_map(&:to_s).join(" ")
+
+ in_root { run_ruby_script("bin/rails generate #{what} #{argument}", verbose: false) }
+ end
+
+ # Runs the supplied rake task
+ #
+ # rake("db:migrate")
+ # rake("db:migrate", env: "production")
+ # rake("gems:install", sudo: true)
+ def rake(command, options={})
+ log :rake, command
+ env = options[:env] || ENV["RAILS_ENV"] || 'development'
+ sudo = options[:sudo] && RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ ? 'sudo ' : ''
+ in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", verbose: false) }
+ end
+
+ # Just run the capify command in root
+ #
+ # capify!
+ def capify!
+ log :capify, ""
+ in_root { run("#{extify(:capify)} .", verbose: false) }
+ end
+
+ # Make an entry in Rails routing file <tt>config/routes.rb</tt>
+ #
+ # route "root 'welcome#index'"
+ def route(routing_code)
+ log :route, routing_code
+ sentinel = /\.routes\.draw do\s*\n/m
+
+ in_root do
+ inject_into_file 'config/routes.rb', " #{routing_code}\n", { after: sentinel, verbose: false, force: false }
+ end
+ end
+
+ # Reads the given file at the source root and prints it in the console.
+ #
+ # readme "README"
+ def readme(path)
+ log File.read(find_in_source_paths(path))
+ end
+
+ # Registers a callback to be executed after bundle and spring binstubs
+ # have run.
+ #
+ # after_bundle do
+ # git add: '.'
+ # end
+ def after_bundle(&block)
+ @after_bundle_callbacks << block
+ end
+
+ protected
+
+ # Define log for backwards compatibility. If just one argument is sent,
+ # invoke say, otherwise invoke say_status. Differently from say and
+ # similarly to say_status, this method respects the quiet? option given.
+ def log(*args)
+ if args.size == 1
+ say args.first.to_s unless options.quiet?
+ else
+ args << (self.behavior == :invoke ? :green : :red)
+ say_status(*args)
+ end
+ end
+
+ # Add an extension to the given name based on the platform.
+ def extify(name)
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
+ "#{name}.bat"
+ else
+ name
+ end
+ end
+
+ # Surround string with single quotes if there is no quotes.
+ # Otherwise fall back to double quotes
+ def quote(value)
+ return value.inspect unless value.is_a? String
+
+ if value.include?("'")
+ value.inspect
+ else
+ "'#{value}'"
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/actions/create_migration.rb b/railties/lib/rails/generators/actions/create_migration.rb
new file mode 100644
index 0000000000..cffdef6ec9
--- /dev/null
+++ b/railties/lib/rails/generators/actions/create_migration.rb
@@ -0,0 +1,69 @@
+require 'thor/actions'
+
+module Rails
+ module Generators
+ module Actions
+ class CreateMigration < Thor::Actions::CreateFile
+
+ def migration_dir
+ File.dirname(@destination)
+ end
+
+ def migration_file_name
+ @base.migration_file_name
+ end
+
+ def identical?
+ exists? && File.binread(existing_migration) == render
+ end
+
+ def revoke!
+ say_destination = exists? ? relative_existing_migration : relative_destination
+ say_status :remove, :red, say_destination
+ return unless exists?
+ ::FileUtils.rm_rf(existing_migration) unless pretend?
+ existing_migration
+ end
+
+ def relative_existing_migration
+ base.relative_to_original_destination_root(existing_migration)
+ end
+
+ def existing_migration
+ @existing_migration ||= begin
+ @base.class.migration_exists?(migration_dir, migration_file_name) ||
+ File.exist?(@destination) && @destination
+ end
+ end
+ alias :exists? :existing_migration
+
+ protected
+
+ def on_conflict_behavior
+ options = base.options.merge(config)
+ if identical?
+ say_status :identical, :blue, relative_existing_migration
+ elsif options[:force]
+ say_status :remove, :green, relative_existing_migration
+ say_status :create, :green
+ unless pretend?
+ ::FileUtils.rm_rf(existing_migration)
+ yield
+ end
+ elsif options[:skip]
+ say_status :skip, :yellow
+ else
+ say_status :conflict, :red
+ raise Error, "Another migration is already named #{migration_file_name}: " +
+ "#{existing_migration}. Use --force to replace this migration " +
+ "or --skip to ignore conflicted file."
+ end
+ end
+
+ def say_status(status, color, message = relative_destination)
+ base.shell.say_status(status, message, color) if config[:verbose]
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb
new file mode 100644
index 0000000000..6183944bb0
--- /dev/null
+++ b/railties/lib/rails/generators/active_model.rb
@@ -0,0 +1,78 @@
+module Rails
+ module Generators
+ # ActiveModel is a class to be implemented by each ORM to allow Rails to
+ # generate customized controller code.
+ #
+ # The API has the same methods as ActiveRecord, but each method returns a
+ # string that matches the ORM API.
+ #
+ # For example:
+ #
+ # ActiveRecord::Generators::ActiveModel.find(Foo, "params[:id]")
+ # # => "Foo.find(params[:id])"
+ #
+ # DataMapper::Generators::ActiveModel.find(Foo, "params[:id]")
+ # # => "Foo.get(params[:id])"
+ #
+ # On initialization, the ActiveModel accepts the instance name that will
+ # receive the calls:
+ #
+ # builder = ActiveRecord::Generators::ActiveModel.new "@foo"
+ # builder.save # => "@foo.save"
+ #
+ # The only exception in ActiveModel for ActiveRecord is the use of self.build
+ # instead of self.new.
+ #
+ class ActiveModel
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ # GET index
+ def self.all(klass)
+ "#{klass}.all"
+ end
+
+ # GET show
+ # GET edit
+ # PATCH/PUT update
+ # DELETE destroy
+ def self.find(klass, params=nil)
+ "#{klass}.find(#{params})"
+ end
+
+ # GET new
+ # POST create
+ def self.build(klass, params=nil)
+ if params
+ "#{klass}.new(#{params})"
+ else
+ "#{klass}.new"
+ end
+ end
+
+ # POST create
+ def save
+ "#{name}.save"
+ end
+
+ # PATCH/PUT update
+ def update(params=nil)
+ "#{name}.update(#{params})"
+ end
+
+ # POST create
+ # PATCH/PUT update
+ def errors
+ "#{name}.errors"
+ end
+
+ # DELETE destroy
+ def destroy
+ "#{name}.destroy"
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
new file mode 100644
index 0000000000..56c9d3e354
--- /dev/null
+++ b/railties/lib/rails/generators/app_base.rb
@@ -0,0 +1,382 @@
+require 'digest/md5'
+require 'active_support/core_ext/string/strip'
+require 'rails/version' unless defined?(Rails::VERSION)
+require 'open-uri'
+require 'uri'
+require 'rails/generators'
+require 'active_support/core_ext/array/extract_options'
+
+module Rails
+ module Generators
+ class AppBase < Base # :nodoc:
+ DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver )
+ JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
+ DATABASES.concat(JDBC_DATABASES)
+
+ attr_accessor :rails_template
+ add_shebang_option!
+
+ argument :app_path, type: :string
+
+ def self.strict_args_position
+ false
+ end
+
+ def self.add_shared_options_for(name)
+ class_option :template, type: :string, aliases: '-m',
+ desc: "Path to some #{name} template (can be a filesystem path or URL)"
+
+ class_option :skip_gemfile, type: :boolean, default: false,
+ desc: "Don't create a Gemfile"
+
+ class_option :skip_bundle, type: :boolean, aliases: '-B', default: false,
+ desc: "Don't run bundle install"
+
+ class_option :skip_git, type: :boolean, aliases: '-G', default: false,
+ desc: 'Skip .gitignore file'
+
+ class_option :skip_keeps, type: :boolean, default: false,
+ desc: 'Skip source control .keep files'
+
+ class_option :skip_action_mailer, type: :boolean, aliases: "-M",
+ default: false,
+ desc: "Skip Action Mailer files"
+
+ class_option :skip_active_record, type: :boolean, aliases: '-O', default: false,
+ desc: 'Skip Active Record files'
+
+ class_option :skip_sprockets, type: :boolean, aliases: '-S', default: false,
+ desc: 'Skip Sprockets files'
+
+ class_option :skip_spring, type: :boolean, default: false,
+ desc: "Don't install Spring application preloader"
+
+ class_option :database, type: :string, aliases: '-d', default: 'sqlite3',
+ desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
+
+ class_option :javascript, type: :string, aliases: '-j', default: 'jquery',
+ desc: 'Preconfigure for selected JavaScript library'
+
+ class_option :skip_javascript, type: :boolean, aliases: '-J', default: false,
+ desc: 'Skip JavaScript files'
+
+ class_option :dev, type: :boolean, default: false,
+ desc: "Setup the #{name} with Gemfile pointing to your Rails checkout"
+
+ class_option :edge, type: :boolean, default: false,
+ desc: "Setup the #{name} with Gemfile pointing to Rails repository"
+
+ class_option :skip_turbolinks, type: :boolean, default: false,
+ desc: 'Skip turbolinks gem'
+
+ class_option :skip_test, type: :boolean, aliases: '-T', default: false,
+ desc: 'Skip test files'
+
+ class_option :rc, type: :string, default: false,
+ desc: "Path to file containing extra configuration options for rails command"
+
+ class_option :no_rc, type: :boolean, default: false,
+ desc: 'Skip loading of extra configuration options from .railsrc file'
+
+ class_option :help, type: :boolean, aliases: '-h', group: :rails,
+ desc: 'Show this help message and quit'
+ end
+
+ def initialize(*args)
+ @gem_filter = lambda { |gem| true }
+ @extra_entries = []
+ super
+ convert_database_option_for_jruby
+ end
+
+ protected
+
+ def gemfile_entry(name, *args)
+ options = args.extract_options!
+ version = args.first
+ github = options[:github]
+ path = options[:path]
+
+ if github
+ @extra_entries << GemfileEntry.github(name, github)
+ elsif path
+ @extra_entries << GemfileEntry.path(name, path)
+ else
+ @extra_entries << GemfileEntry.version(name, version)
+ end
+ self
+ end
+
+ def gemfile_entries
+ [rails_gemfile_entry,
+ database_gemfile_entry,
+ assets_gemfile_entry,
+ javascript_gemfile_entry,
+ jbuilder_gemfile_entry,
+ psych_gemfile_entry,
+ @extra_entries].flatten.find_all(&@gem_filter)
+ end
+
+ def add_gem_entry_filter
+ @gem_filter = lambda { |next_filter, entry|
+ yield(entry) && next_filter.call(entry)
+ }.curry[@gem_filter]
+ end
+
+ def builder
+ @builder ||= begin
+ builder_class = get_builder_class
+ builder_class.include(ActionMethods)
+ builder_class.new(self)
+ end
+ end
+
+ def build(meth, *args)
+ builder.send(meth, *args) if builder.respond_to?(meth)
+ end
+
+ def create_root
+ valid_const?
+
+ empty_directory '.'
+ FileUtils.cd(destination_root) unless options[:pretend]
+ end
+
+ def apply_rails_template
+ apply rails_template if rails_template
+ rescue Thor::Error, LoadError, Errno::ENOENT => e
+ raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}"
+ end
+
+ def set_default_accessors!
+ self.destination_root = File.expand_path(app_path, destination_root)
+ self.rails_template = case options[:template]
+ when /^https?:\/\//
+ options[:template]
+ when String
+ File.expand_path(options[:template], Dir.pwd)
+ else
+ options[:template]
+ end
+ end
+
+ def database_gemfile_entry
+ return [] if options[:skip_active_record]
+ gem_name, gem_version = gem_for_database
+ GemfileEntry.version gem_name, gem_version,
+ "Use #{options[:database]} as the database for Active Record"
+ end
+
+ def include_all_railties?
+ options.values_at(:skip_active_record, :skip_action_mailer, :skip_test, :skip_sprockets).none?
+ end
+
+ def comment_if(value)
+ options[value] ? '# ' : ''
+ end
+
+ def keeps?
+ !options[:skip_keeps]
+ end
+
+ def sqlite3?
+ !options[:skip_active_record] && options[:database] == 'sqlite3'
+ end
+
+ class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
+ def initialize(name, version, comment, options = {}, commented_out = false)
+ super
+ end
+
+ def self.github(name, github, branch = nil, comment = nil)
+ if branch
+ new(name, nil, comment, github: github, branch: branch)
+ else
+ new(name, nil, comment, github: github)
+ end
+ end
+
+ def self.version(name, version, comment = nil)
+ new(name, version, comment)
+ end
+
+ def self.path(name, path, comment = nil)
+ new(name, nil, comment, path: path)
+ end
+
+ def version
+ version = super
+
+ if version.is_a?(Array)
+ version.join("', '")
+ else
+ version
+ end
+ end
+ end
+
+ def rails_gemfile_entry
+ dev_edge_common = [
+ GemfileEntry.github('sprockets-rails', 'rails/sprockets-rails'),
+ GemfileEntry.github('sprockets', 'rails/sprockets'),
+ GemfileEntry.github('sass-rails', 'rails/sass-rails'),
+ GemfileEntry.github('arel', 'rails/arel'),
+ GemfileEntry.github('rack', 'rack/rack')
+ ]
+ if options.dev?
+ [
+ GemfileEntry.path('rails', Rails::Generators::RAILS_DEV_PATH)
+ ] + dev_edge_common
+ elsif options.edge?
+ [
+ GemfileEntry.github('rails', 'rails/rails')
+ ] + dev_edge_common
+ else
+ [GemfileEntry.version('rails',
+ Rails::VERSION::STRING,
+ "Bundle edge Rails instead: gem 'rails', github: 'rails/rails'")]
+ end
+ end
+
+ def gem_for_database
+ # %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql )
+ case options[:database]
+ when "oracle" then ["ruby-oci8", nil]
+ when "postgresql" then ["pg", ["~> 0.18"]]
+ when "frontbase" then ["ruby-frontbase", nil]
+ when "mysql" then ["mysql2", [">= 0.3.18", "< 0.5"]]
+ when "sqlserver" then ["activerecord-sqlserver-adapter", nil]
+ when "jdbcmysql" then ["activerecord-jdbcmysql-adapter", nil]
+ when "jdbcsqlite3" then ["activerecord-jdbcsqlite3-adapter", nil]
+ when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil]
+ when "jdbc" then ["activerecord-jdbc-adapter", nil]
+ else [options[:database], nil]
+ end
+ end
+
+ def convert_database_option_for_jruby
+ if defined?(JRUBY_VERSION)
+ case options[:database]
+ when "oracle" then options[:database].replace "jdbc"
+ when "postgresql" then options[:database].replace "jdbcpostgresql"
+ when "mysql" then options[:database].replace "jdbcmysql"
+ when "sqlite3" then options[:database].replace "jdbcsqlite3"
+ end
+ end
+ end
+
+ def assets_gemfile_entry
+ return [] if options[:skip_sprockets]
+
+ gems = []
+
+ gems << GemfileEntry.version('uglifier',
+ '>= 1.3.0',
+ 'Use Uglifier as compressor for JavaScript assets')
+
+ gems
+ end
+
+ def jbuilder_gemfile_entry
+ return [] if options[:api]
+
+ comment = 'Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder'
+ GemfileEntry.version('jbuilder', '~> 2.0', comment)
+ end
+
+ def coffee_gemfile_entry
+ comment = 'Use CoffeeScript for .coffee assets and views'
+ if options.dev? || options.edge?
+ GemfileEntry.github 'coffee-rails', 'rails/coffee-rails', nil, comment
+ else
+ GemfileEntry.version 'coffee-rails', '~> 4.1.0', comment
+ end
+ end
+
+ def javascript_gemfile_entry
+ if options[:skip_javascript]
+ []
+ else
+ gems = [coffee_gemfile_entry, javascript_runtime_gemfile_entry]
+ gems << GemfileEntry.version("#{options[:javascript]}-rails", nil,
+ "Use #{options[:javascript]} as the JavaScript library")
+
+ unless options[:skip_turbolinks]
+ gems << GemfileEntry.version("turbolinks", nil,
+ "Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks")
+ end
+
+ gems
+ end
+ end
+
+ def javascript_runtime_gemfile_entry
+ comment = 'See https://github.com/rails/execjs#readme for more supported runtimes'
+ if defined?(JRUBY_VERSION)
+ GemfileEntry.version 'therubyrhino', nil, comment
+ else
+ GemfileEntry.new 'therubyracer', nil, comment, { platforms: :ruby }, true
+ end
+ end
+
+ def psych_gemfile_entry
+ return [] unless defined?(Rubinius)
+
+ comment = 'Use Psych as the YAML engine, instead of Syck, so serialized ' \
+ 'data can be read safely from different rubies (see http://git.io/uuLVag)'
+ GemfileEntry.new('psych', '~> 2.0', comment, platforms: :rbx)
+ end
+
+ def bundle_command(command)
+ say_status :run, "bundle #{command}"
+
+ # We are going to shell out rather than invoking Bundler::CLI.new(command)
+ # because `rails new` loads the Thor gem and on the other hand bundler uses
+ # its own vendored Thor, which could be a different version. Running both
+ # things in the same process is a recipe for a night with paracetamol.
+ #
+ # We unset temporary bundler variables to load proper bundler and Gemfile.
+ #
+ # Thanks to James Tucker for the Gem tricks involved in this call.
+ _bundle_command = Gem.bin_path('bundler', 'bundle')
+
+ require 'bundler'
+ Bundler.with_clean_env do
+ full_command = %Q["#{Gem.ruby}" "#{_bundle_command}" #{command}]
+ if options[:quiet]
+ system(full_command, out: File::NULL)
+ else
+ system(full_command)
+ end
+ end
+ end
+
+ def bundle_install?
+ !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
+ end
+
+ def spring_install?
+ !options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
+ end
+
+ def run_bundle
+ bundle_command('install') if bundle_install?
+ end
+
+ def generate_spring_binstubs
+ if bundle_install? && spring_install?
+ bundle_command("exec spring binstub --all")
+ end
+ end
+
+ def empty_directory_with_keep_file(destination, config = {})
+ empty_directory(destination, config)
+ keep_file(destination)
+ end
+
+ def keep_file(destination)
+ create_file("#{destination}/.keep") if keeps?
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
new file mode 100644
index 0000000000..c72ec400a0
--- /dev/null
+++ b/railties/lib/rails/generators/base.rb
@@ -0,0 +1,379 @@
+begin
+ require 'thor/group'
+rescue LoadError
+ puts "Thor is not available.\nIf you ran this command from a git checkout " \
+ "of Rails, please make sure thor is installed,\nand run this command " \
+ "as `ruby #{$0} #{(ARGV | ['--dev']).join(" ")}`"
+ exit
+end
+
+module Rails
+ module Generators
+ class Error < Thor::Error # :nodoc:
+ end
+
+ class Base < Thor::Group
+ include Thor::Actions
+ include Rails::Generators::Actions
+
+ add_runtime_options!
+ strict_args_position!
+
+ # Returns the source root for this generator using default_source_root as default.
+ def self.source_root(path=nil)
+ @_source_root = path if path
+ @_source_root ||= default_source_root
+ end
+
+ # Tries to get the description from a USAGE file one folder above the source
+ # root otherwise uses a default description.
+ def self.desc(description=nil)
+ return super if description
+
+ @desc ||= if usage_path
+ ERB.new(File.read(usage_path)).result(binding)
+ else
+ "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
+ end
+ end
+
+ # Convenience method to get the namespace from the class name. It's the
+ # same as Thor default except that the Generator at the end of the class
+ # is removed.
+ def self.namespace(name=nil)
+ return super if name
+ @namespace ||= super.sub(/_generator$/, '').sub(/:generators:/, ':')
+ end
+
+ # Convenience method to hide this generator from the available ones when
+ # running rails generator command.
+ def self.hide!
+ Rails::Generators.hide_namespace self.namespace
+ end
+
+ # Invoke a generator based on the value supplied by the user to the
+ # given option named "name". A class option is created when this method
+ # is invoked and you can set a hash to customize it.
+ #
+ # ==== Examples
+ #
+ # module Rails::Generators
+ # class ControllerGenerator < Base
+ # hook_for :test_framework, aliases: "-t"
+ # end
+ # end
+ #
+ # The example above will create a test framework option and will invoke
+ # a generator based on the user supplied value.
+ #
+ # For example, if the user invoke the controller generator as:
+ #
+ # rails generate controller Account --test-framework=test_unit
+ #
+ # The controller generator will then try to invoke the following generators:
+ #
+ # "rails:test_unit", "test_unit:controller", "test_unit"
+ #
+ # Notice that "rails:generators:test_unit" could be loaded as well, what
+ # Rails looks for is the first and last parts of the namespace. This is what
+ # allows any test framework to hook into Rails as long as it provides any
+ # of the hooks above.
+ #
+ # ==== Options
+ #
+ # The first and last part used to find the generator to be invoked are
+ # guessed based on class invokes hook_for, as noticed in the example above.
+ # This can be customized with two options: :in and :as.
+ #
+ # Let's suppose you are creating a generator that needs to invoke the
+ # controller generator from test unit. Your first attempt is:
+ #
+ # class AwesomeGenerator < Rails::Generators::Base
+ # hook_for :test_framework
+ # end
+ #
+ # The lookup in this case for test_unit as input is:
+ #
+ # "test_unit:awesome", "test_unit"
+ #
+ # Which is not the desired lookup. You can change it by providing the
+ # :as option:
+ #
+ # class AwesomeGenerator < Rails::Generators::Base
+ # hook_for :test_framework, as: :controller
+ # end
+ #
+ # And now it will look up at:
+ #
+ # "test_unit:controller", "test_unit"
+ #
+ # Similarly, if you want it to also look up in the rails namespace, you
+ # just need to provide the :in value:
+ #
+ # class AwesomeGenerator < Rails::Generators::Base
+ # hook_for :test_framework, in: :rails, as: :controller
+ # end
+ #
+ # And the lookup is exactly the same as previously:
+ #
+ # "rails:test_unit", "test_unit:controller", "test_unit"
+ #
+ # ==== Switches
+ #
+ # All hooks come with switches for user interface. If you do not want
+ # to use any test framework, you can do:
+ #
+ # rails generate controller Account --skip-test-framework
+ #
+ # Or similarly:
+ #
+ # rails generate controller Account --no-test-framework
+ #
+ # ==== Boolean hooks
+ #
+ # In some cases, you may want to provide a boolean hook. For example, webrat
+ # developers might want to have webrat available on controller generator.
+ # This can be achieved as:
+ #
+ # Rails::Generators::ControllerGenerator.hook_for :webrat, type: :boolean
+ #
+ # Then, if you want webrat to be invoked, just supply:
+ #
+ # rails generate controller Account --webrat
+ #
+ # The hooks lookup is similar as above:
+ #
+ # "rails:generators:webrat", "webrat:generators:controller", "webrat"
+ #
+ # ==== Custom invocations
+ #
+ # You can also supply a block to hook_for to customize how the hook is
+ # going to be invoked. The block receives two arguments, an instance
+ # of the current class and the class to be invoked.
+ #
+ # For example, in the resource generator, the controller should be invoked
+ # with a pluralized class name. But by default it is invoked with the same
+ # name as the resource generator, which is singular. To change this, we
+ # can give a block to customize how the controller can be invoked.
+ #
+ # hook_for :resource_controller do |instance, controller|
+ # instance.invoke controller, [ instance.name.pluralize ]
+ # end
+ #
+ def self.hook_for(*names, &block)
+ options = names.extract_options!
+ in_base = options.delete(:in) || base_name
+ as_hook = options.delete(:as) || generator_name
+
+ names.each do |name|
+ unless class_options.key?(name)
+ defaults = if options[:type] == :boolean
+ { }
+ elsif [true, false].include?(default_value_for_option(name, options))
+ { banner: "" }
+ else
+ { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
+ end
+
+ class_option(name, defaults.merge!(options))
+ end
+
+ hooks[name] = [ in_base, as_hook ]
+ invoke_from_option(name, options, &block)
+ end
+ end
+
+ # Remove a previously added hook.
+ #
+ # remove_hook_for :orm
+ def self.remove_hook_for(*names)
+ remove_invocation(*names)
+
+ names.each do |name|
+ hooks.delete(name)
+ end
+ end
+
+ # Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
+ def self.class_option(name, options={}) #:nodoc:
+ options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
+ options[:aliases] = default_aliases_for_option(name, options)
+ options[:default] = default_value_for_option(name, options)
+ super(name, options)
+ end
+
+ # Returns the default source root for a given generator. This is used internally
+ # by rails to set its generators source root. If you want to customize your source
+ # root, you should use source_root.
+ def self.default_source_root
+ return unless base_name && generator_name
+ return unless default_generator_root
+ path = File.join(default_generator_root, 'templates')
+ path if File.exist?(path)
+ end
+
+ # Returns the base root for a common set of generators. This is used to dynamically
+ # guess the default source root.
+ def self.base_root
+ File.dirname(__FILE__)
+ end
+
+ # Cache source root and add lib/generators/base/generator/templates to
+ # source paths.
+ def self.inherited(base) #:nodoc:
+ super
+
+ # Invoke source_root so the default_source_root is set.
+ base.source_root
+
+ if base.name && base.name !~ /Base$/
+ Rails::Generators.subclasses << base
+
+ Rails::Generators.templates_path.each do |path|
+ if base.name.include?('::')
+ base.source_paths << File.join(path, base.base_name, base.generator_name)
+ else
+ base.source_paths << File.join(path, base.generator_name)
+ end
+ end
+ end
+ end
+
+ protected
+
+ # Check whether the given class names are already taken by user
+ # application or Ruby on Rails.
+ def class_collisions(*class_names) #:nodoc:
+ return unless behavior == :invoke
+
+ class_names.flatten.each do |class_name|
+ class_name = class_name.to_s
+ next if class_name.strip.empty?
+
+ # Split the class from its module nesting
+ nesting = class_name.split('::')
+ last_name = nesting.pop
+ last = extract_last_module(nesting)
+
+ if last && last.const_defined?(last_name.camelize, false)
+ raise Error, "The name '#{class_name}' is either already used in your application " <<
+ "or reserved by Ruby on Rails. Please choose an alternative and run " <<
+ "this generator again."
+ end
+ end
+ end
+
+ # Takes in an array of nested modules and extracts the last module
+ def extract_last_module(nesting)
+ nesting.inject(Object) do |last_module, nest|
+ break unless last_module.const_defined?(nest, false)
+ last_module.const_get(nest)
+ end
+ end
+
+ # Use Rails default banner.
+ def self.banner
+ "rails generate #{namespace.sub(/^rails:/,'')} #{self.arguments.map(&:usage).join(' ')} [options]".gsub(/\s+/, ' ')
+ end
+
+ # Sets the base_name taking into account the current class namespace.
+ def self.base_name
+ @base_name ||= begin
+ if base = name.to_s.split('::').first
+ base.underscore
+ end
+ end
+ end
+
+ # Removes the namespaces and get the generator name. For example,
+ # Rails::Generators::ModelGenerator will return "model" as generator name.
+ def self.generator_name
+ @generator_name ||= begin
+ if generator = name.to_s.split('::').last
+ generator.sub!(/Generator$/, '')
+ generator.underscore
+ end
+ end
+ end
+
+ # Returns the default value for the option name given doing a lookup in
+ # Rails::Generators.options.
+ def self.default_value_for_option(name, options)
+ default_for_option(Rails::Generators.options, name, options, options[:default])
+ end
+
+ # Returns default aliases for the option name given doing a lookup in
+ # Rails::Generators.aliases.
+ def self.default_aliases_for_option(name, options)
+ default_for_option(Rails::Generators.aliases, name, options, options[:aliases])
+ end
+
+ # Returns default for the option name given doing a lookup in config.
+ def self.default_for_option(config, name, options, default)
+ if generator_name and c = config[generator_name.to_sym] and c.key?(name)
+ c[name]
+ elsif base_name and c = config[base_name.to_sym] and c.key?(name)
+ c[name]
+ elsif config[:rails].key?(name)
+ config[:rails][name]
+ else
+ default
+ end
+ end
+
+ # Keep hooks configuration that are used on prepare_for_invocation.
+ def self.hooks #:nodoc:
+ @hooks ||= from_superclass(:hooks, {})
+ end
+
+ # Prepare class invocation to search on Rails namespace if a previous
+ # added hook is being used.
+ def self.prepare_for_invocation(name, value) #:nodoc:
+ return super unless value.is_a?(String) || value.is_a?(Symbol)
+
+ if value && constants = self.hooks[name]
+ value = name if TrueClass === value
+ Rails::Generators.find_by_namespace(value, *constants)
+ elsif klass = Rails::Generators.find_by_namespace(value)
+ klass
+ else
+ super
+ end
+ end
+
+ # Small macro to add ruby as an option to the generator with proper
+ # default value plus an instance helper method called shebang.
+ def self.add_shebang_option!
+ class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command,
+ desc: "Path to the Ruby binary of your choice", banner: "PATH"
+
+ no_tasks {
+ define_method :shebang do
+ @shebang ||= begin
+ command = if options[:ruby] == Thor::Util.ruby_command
+ "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}"
+ else
+ options[:ruby]
+ end
+ "#!#{command}"
+ end
+ end
+ }
+ end
+
+ def self.usage_path
+ paths = [
+ source_root && File.expand_path("../USAGE", source_root),
+ default_generator_root && File.join(default_generator_root, "USAGE")
+ ]
+ paths.compact.detect { |path| File.exist? path }
+ end
+
+ def self.default_generator_root
+ path = File.expand_path(File.join(base_name, generator_name), base_root)
+ path if File.exist?(path)
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/css/assets/assets_generator.rb b/railties/lib/rails/generators/css/assets/assets_generator.rb
new file mode 100644
index 0000000000..e4a305f4b3
--- /dev/null
+++ b/railties/lib/rails/generators/css/assets/assets_generator.rb
@@ -0,0 +1,13 @@
+require "rails/generators/named_base"
+
+module Css # :nodoc:
+ module Generators # :nodoc:
+ class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
+ source_root File.expand_path("../templates", __FILE__)
+
+ def copy_stylesheet
+ copy_file "stylesheet.css", File.join('app/assets/stylesheets', class_path, "#{file_name}.css")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/css/assets/templates/stylesheet.css b/railties/lib/rails/generators/css/assets/templates/stylesheet.css
new file mode 100644
index 0000000000..afad32db02
--- /dev/null
+++ b/railties/lib/rails/generators/css/assets/templates/stylesheet.css
@@ -0,0 +1,4 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+*/
diff --git a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..cf534030f9
--- /dev/null
+++ b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
@@ -0,0 +1,16 @@
+require "rails/generators/named_base"
+
+module Css # :nodoc:
+ module Generators # :nodoc:
+ class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc:
+ # In order to allow the Sass generators to pick up the default Rails CSS and
+ # transform it, we leave it in a standard location for the CSS stylesheet
+ # generators to handle. For the simple, default case, just copy it over.
+ def copy_stylesheet
+ dir = Rails::Generators::ScaffoldGenerator.source_root
+ file = File.join(dir, "scaffold.css")
+ create_file "app/assets/stylesheets/scaffold.css", File.read(file)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb
new file mode 100644
index 0000000000..0755ac335c
--- /dev/null
+++ b/railties/lib/rails/generators/erb.rb
@@ -0,0 +1,25 @@
+require 'rails/generators/named_base'
+
+module Erb # :nodoc:
+ module Generators # :nodoc:
+ class Base < Rails::Generators::NamedBase #:nodoc:
+ protected
+
+ def formats
+ [format]
+ end
+
+ def format
+ :html
+ end
+
+ def handler
+ :erb
+ end
+
+ def filename_with_extensions(name, format = self.format)
+ [name, format, handler].compact.join(".")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/erb/controller/controller_generator.rb b/railties/lib/rails/generators/erb/controller/controller_generator.rb
new file mode 100644
index 0000000000..94c1b835d1
--- /dev/null
+++ b/railties/lib/rails/generators/erb/controller/controller_generator.rb
@@ -0,0 +1,22 @@
+require 'rails/generators/erb'
+
+module Erb # :nodoc:
+ module Generators # :nodoc:
+ class ControllerGenerator < Base # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
+
+ def copy_view_files
+ base_path = File.join("app/views", class_path, file_name)
+ empty_directory base_path
+
+ actions.each do |action|
+ @action = action
+ formats.each do |format|
+ @path = File.join(base_path, filename_with_extensions(action, format))
+ template filename_with_extensions(:view, format), @path
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/erb/controller/templates/view.html.erb b/railties/lib/rails/generators/erb/controller/templates/view.html.erb
new file mode 100644
index 0000000000..cd54d13d83
--- /dev/null
+++ b/railties/lib/rails/generators/erb/controller/templates/view.html.erb
@@ -0,0 +1,2 @@
+<h1><%= class_name %>#<%= @action %></h1>
+<p>Find me in <%= @path %></p>
diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
new file mode 100644
index 0000000000..65563aa6db
--- /dev/null
+++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
@@ -0,0 +1,40 @@
+require 'rails/generators/erb'
+
+module Erb # :nodoc:
+ module Generators # :nodoc:
+ class MailerGenerator < Base # :nodoc:
+ argument :actions, type: :array, default: [], banner: "method method"
+
+ def copy_view_files
+ view_base_path = File.join("app/views", class_path, file_name + '_mailer')
+ empty_directory view_base_path
+
+ if self.behavior == :invoke
+ formats.each do |format|
+ layout_path = File.join("app/views/layouts", filename_with_extensions("mailer", format))
+ template filename_with_extensions(:layout, format), layout_path
+ end
+ end
+
+ actions.each do |action|
+ @action = action
+
+ formats.each do |format|
+ @path = File.join(view_base_path, filename_with_extensions(action, format))
+ template filename_with_extensions(:view, format), @path
+ end
+ end
+ end
+
+ protected
+
+ def formats
+ [:text, :html]
+ end
+
+ def file_name
+ @_file_name ||= super.gsub(/\_mailer/i, '')
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/erb/mailer/templates/layout.html.erb b/railties/lib/rails/generators/erb/mailer/templates/layout.html.erb
new file mode 100644
index 0000000000..93110e74ad
--- /dev/null
+++ b/railties/lib/rails/generators/erb/mailer/templates/layout.html.erb
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ <%%= yield %>
+ </body>
+</html>
diff --git a/railties/lib/rails/generators/erb/mailer/templates/layout.text.erb b/railties/lib/rails/generators/erb/mailer/templates/layout.text.erb
new file mode 100644
index 0000000000..6363733e6e
--- /dev/null
+++ b/railties/lib/rails/generators/erb/mailer/templates/layout.text.erb
@@ -0,0 +1 @@
+<%%= yield %>
diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.html.erb b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb
new file mode 100644
index 0000000000..b5045671b3
--- /dev/null
+++ b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb
@@ -0,0 +1,5 @@
+<h1><%= class_name %>#<%= @action %></h1>
+
+<p>
+ <%%= @greeting %>, find me in <%= @path %>
+</p>
diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.text.erb b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb
new file mode 100644
index 0000000000..342285df19
--- /dev/null
+++ b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb
@@ -0,0 +1,3 @@
+<%= class_name %>#<%= @action %>
+
+<%%= @greeting %>, find me in <%= @path %>
diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..c94829a0ae
--- /dev/null
+++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
@@ -0,0 +1,31 @@
+require 'rails/generators/erb'
+require 'rails/generators/resource_helpers'
+
+module Erb # :nodoc:
+ module Generators # :nodoc:
+ class ScaffoldGenerator < Base # :nodoc:
+ include Rails::Generators::ResourceHelpers
+
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
+
+ def create_root_folder
+ empty_directory File.join("app/views", controller_file_path)
+ end
+
+ def copy_view_files
+ available_views.each do |view|
+ formats.each do |format|
+ filename = filename_with_extensions(view, format)
+ template filename, File.join("app/views", controller_file_path, filename)
+ end
+ end
+ end
+
+ protected
+
+ def available_views
+ %w(index edit show new _form)
+ end
+ 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
new file mode 100644
index 0000000000..519b6c8603
--- /dev/null
+++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
@@ -0,0 +1,34 @@
+<%%= form_for(<%= singular_table_name %>) do |f| %>
+ <%% 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>
+
+ <ul>
+ <%% <%= singular_table_name %>.errors.full_messages.each do |message| %>
+ <li><%%= message %></li>
+ <%% end %>
+ </ul>
+ </div>
+ <%% end %>
+
+<% attributes.each do |attribute| -%>
+ <div class="field">
+<% if attribute.password_digest? -%>
+ <%%= f.label :password %>
+ <%%= f.password_field :password %>
+ </div>
+
+ <div class="field">
+ <%%= f.label :password_confirmation %>
+ <%%= f.password_field :password_confirmation %>
+<% else -%>
+ <%%= f.label :<%= attribute.column_name %> %>
+ <%%= f.<%= attribute.field_type %> :<%= attribute.column_name %> %>
+<% end -%>
+ </div>
+
+<% end -%>
+ <div class="actions">
+ <%%= f.submit %>
+ </div>
+<%% end %>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb
new file mode 100644
index 0000000000..81329473d9
--- /dev/null
+++ b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb
@@ -0,0 +1,6 @@
+<h1>Editing <%= singular_table_name.titleize %></h1>
+
+<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %>
+
+<%%= link_to 'Show', @<%= singular_table_name %> %> |
+<%%= link_to 'Back', <%= index_helper %>_path %>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
new file mode 100644
index 0000000000..5f4904fee1
--- /dev/null
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
@@ -0,0 +1,31 @@
+<p id="notice"><%%= notice %></p>
+
+<h1><%= plural_table_name.titleize %></h1>
+
+<table>
+ <thead>
+ <tr>
+<% attributes.reject(&:password_digest?).each do |attribute| -%>
+ <th><%= attribute.human_name %></th>
+<% end -%>
+ <th colspan="3"></th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
+ <tr>
+<% attributes.reject(&:password_digest?).each do |attribute| -%>
+ <td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td>
+<% end -%>
+ <td><%%= link_to 'Show', <%= singular_table_name %> %></td>
+ <td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td>
+ <td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
+ </tr>
+ <%% end %>
+ </tbody>
+</table>
+
+<br>
+
+<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_table_name %>_path %>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb
new file mode 100644
index 0000000000..9b2b2f4875
--- /dev/null
+++ b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb
@@ -0,0 +1,5 @@
+<h1>New <%= singular_table_name.titleize %></h1>
+
+<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %>
+
+<%%= link_to 'Back', <%= index_helper %>_path %>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
new file mode 100644
index 0000000000..5e634153be
--- /dev/null
+++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
@@ -0,0 +1,11 @@
+<p id="notice"><%%= notice %></p>
+
+<% attributes.reject(&:password_digest?).each do |attribute| -%>
+<p>
+ <strong><%= attribute.human_name %>:</strong>
+ <%%= @<%= singular_table_name %>.<%= attribute.name %> %>
+</p>
+
+<% end -%>
+<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>) %> |
+<%%= link_to 'Back', <%= index_helper %>_path %>
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
new file mode 100644
index 0000000000..8145a26e22
--- /dev/null
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -0,0 +1,174 @@
+require 'active_support/time'
+
+module Rails
+ module Generators
+ class GeneratedAttribute # :nodoc:
+ INDEX_OPTIONS = %w(index uniq)
+ UNIQ_INDEX_OPTIONS = %w(uniq)
+
+ attr_accessor :name, :type
+ attr_reader :attr_options
+ attr_writer :index_name
+
+ class << self
+ def parse(column_definition)
+ name, type, has_index = column_definition.split(':')
+
+ # if user provided "name:index" instead of "name:string:index"
+ # type should be set blank so GeneratedAttribute's constructor
+ # could set it to :string
+ has_index, type = type, nil if INDEX_OPTIONS.include?(type)
+
+ type, attr_options = *parse_type_and_options(type)
+ type = type.to_sym if type
+
+ if type && reference?(type)
+ references_index = UNIQ_INDEX_OPTIONS.include?(has_index) ? { unique: true } : true
+ attr_options[:index] = references_index
+ end
+
+ new(name, type, has_index, attr_options)
+ end
+
+ def reference?(type)
+ [:references, :belongs_to].include? type
+ end
+
+ private
+
+ # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
+ # when declaring options curly brackets should be used
+ def parse_type_and_options(type)
+ case type
+ when /(string|text|binary|integer)\{(\d+)\}/
+ return $1, limit: $2.to_i
+ when /decimal\{(\d+)[,.-](\d+)\}/
+ return :decimal, precision: $1.to_i, scale: $2.to_i
+ when /(references|belongs_to)\{(.+)\}/
+ type = $1
+ provided_options = $2.split(/[,.-]/)
+ options = Hash[provided_options.map { |opt| [opt.to_sym, true] }]
+ return type, options
+ else
+ return type, {}
+ end
+ end
+ end
+
+ def initialize(name, type=nil, index_type=false, attr_options={})
+ @name = name
+ @type = type || :string
+ @has_index = INDEX_OPTIONS.include?(index_type)
+ @has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type)
+ @attr_options = attr_options
+ end
+
+ def field_type
+ @field_type ||= case type
+ when :integer then :number_field
+ when :float, :decimal then :text_field
+ when :time then :time_select
+ when :datetime, :timestamp then :datetime_select
+ when :date then :date_select
+ when :text then :text_area
+ when :boolean then :check_box
+ else
+ :text_field
+ end
+ end
+
+ def default
+ @default ||= case type
+ when :integer then 1
+ when :float then 1.5
+ when :decimal then "9.99"
+ when :datetime, :timestamp, :time then Time.now.to_s(:db)
+ when :date then Date.today.to_s(:db)
+ when :string then name == "type" ? "" : "MyString"
+ when :text then "MyText"
+ when :boolean then false
+ when :references, :belongs_to then nil
+ else
+ ""
+ end
+ end
+
+ def plural_name
+ name.sub(/_id$/, '').pluralize
+ end
+
+ def singular_name
+ name.sub(/_id$/, '').singularize
+ end
+
+ def human_name
+ name.humanize
+ end
+
+ def index_name
+ @index_name ||= if polymorphic?
+ %w(id type).map { |t| "#{name}_#{t}" }
+ else
+ column_name
+ end
+ end
+
+ def column_name
+ @column_name ||= reference? ? "#{name}_id" : name
+ end
+
+ def foreign_key?
+ !!(name =~ /_id$/)
+ end
+
+ def reference?
+ self.class.reference?(type)
+ end
+
+ def polymorphic?
+ self.attr_options[:polymorphic]
+ end
+
+ def required?
+ self.attr_options[:required]
+ end
+
+ def has_index?
+ @has_index
+ end
+
+ def has_uniq_index?
+ @has_uniq_index
+ end
+
+ def password_digest?
+ name == 'password' && type == :digest
+ end
+
+ def token?
+ type == :token
+ end
+
+ def inject_options
+ "".tap { |s| options_for_migration.each { |k,v| s << ", #{k}: #{v.inspect}" } }
+ end
+
+ def inject_index_options
+ has_uniq_index? ? ", unique: true" : ""
+ end
+
+ def options_for_migration
+ @attr_options.dup.tap do |options|
+ if required?
+ options.delete(:required)
+ options[:null] = false
+ end
+
+ if reference? && !polymorphic?
+ options[:foreign_key] = true
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb
new file mode 100644
index 0000000000..1e925b2cd2
--- /dev/null
+++ b/railties/lib/rails/generators/js/assets/assets_generator.rb
@@ -0,0 +1,13 @@
+require "rails/generators/named_base"
+
+module Js # :nodoc:
+ module Generators # :nodoc:
+ class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
+ source_root File.expand_path("../templates", __FILE__)
+
+ def copy_javascript
+ copy_file "javascript.js", File.join('app/assets/javascripts', class_path, "#{file_name}.js")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/js/assets/templates/javascript.js b/railties/lib/rails/generators/js/assets/templates/javascript.js
new file mode 100644
index 0000000000..dee720facd
--- /dev/null
+++ b/railties/lib/rails/generators/js/assets/templates/javascript.js
@@ -0,0 +1,2 @@
+// Place all the behaviors and hooks related to the matching controller here.
+// All this logic will automatically be available in application.js.
diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb
new file mode 100644
index 0000000000..87f2e1d42b
--- /dev/null
+++ b/railties/lib/rails/generators/migration.rb
@@ -0,0 +1,69 @@
+require 'active_support/concern'
+require 'rails/generators/actions/create_migration'
+
+module Rails
+ module Generators
+ # Holds common methods for migrations. It assumes that migrations have the
+ # [0-9]*_name format and can be used by other frameworks (like Sequel)
+ # just by implementing the next migration version method.
+ module Migration
+ extend ActiveSupport::Concern
+ attr_reader :migration_number, :migration_file_name, :migration_class_name
+
+ module ClassMethods #:nodoc:
+ def migration_lookup_at(dirname)
+ Dir.glob("#{dirname}/[0-9]*_*.rb")
+ end
+
+ def migration_exists?(dirname, file_name)
+ migration_lookup_at(dirname).grep(/\d+_#{file_name}.rb$/).first
+ end
+
+ def current_migration_number(dirname)
+ migration_lookup_at(dirname).collect do |file|
+ File.basename(file).split("_").first.to_i
+ end.max.to_i
+ end
+
+ def next_migration_number(dirname)
+ raise NotImplementedError
+ end
+ end
+
+ def create_migration(destination, data, config = {}, &block)
+ action Rails::Generators::Actions::CreateMigration.new(self, destination, block || data.to_s, config)
+ end
+
+ def set_migration_assigns!(destination)
+ destination = File.expand_path(destination, self.destination_root)
+
+ migration_dir = File.dirname(destination)
+ @migration_number = self.class.next_migration_number(migration_dir)
+ @migration_file_name = File.basename(destination, '.rb')
+ @migration_class_name = @migration_file_name.camelize
+ end
+
+ # Creates a migration template at the given destination. The difference
+ # to the default template method is that the migration version is appended
+ # to the destination file name.
+ #
+ # The migration version, migration file name, migration class name are
+ # available as instance variables in the template to be rendered.
+ #
+ # migration_template "migration.rb", "db/migrate/add_foo_to_bar.rb"
+ def migration_template(source, destination, config = {})
+ source = File.expand_path(find_in_source_paths(source.to_s))
+
+ set_migration_assigns!(destination)
+ context = instance_eval('binding')
+
+ dir, base = File.split(destination)
+ numbered_destination = File.join(dir, ["%migration_number%", base].join('_'))
+
+ create_migration numbered_destination, nil, config do
+ ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb
new file mode 100644
index 0000000000..42c646543e
--- /dev/null
+++ b/railties/lib/rails/generators/model_helpers.rb
@@ -0,0 +1,28 @@
+require 'rails/generators/active_model'
+
+module Rails
+ module Generators
+ module ModelHelpers # :nodoc:
+ PLURAL_MODEL_NAME_WARN_MESSAGE = "[WARNING] The model name '%s' was recognized as a plural, using the singular '%s' instead. " \
+ "Override with --force-plural or setup custom inflection rules for this noun before running the generator."
+ mattr_accessor :skip_warn
+
+ def self.included(base) #:nodoc:
+ base.class_option :force_plural, type: :boolean, default: false, desc: 'Forces the use of the given model name'
+ end
+
+ def initialize(args, *_options)
+ super
+ if name == name.pluralize && name.singularize != name.pluralize && !options[:force_plural]
+ singular = name.singularize
+ unless ModelHelpers.skip_warn
+ say PLURAL_MODEL_NAME_WARN_MESSAGE % [name, singular]
+ ModelHelpers.skip_warn = true
+ end
+ name.replace singular
+ assign_names!(name)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
new file mode 100644
index 0000000000..243694f38e
--- /dev/null
+++ b/railties/lib/rails/generators/named_base.rb
@@ -0,0 +1,213 @@
+require 'active_support/core_ext/module/introspection'
+require 'rails/generators/base'
+require 'rails/generators/generated_attribute'
+
+module Rails
+ module Generators
+ class NamedBase < Base
+ argument :name, type: :string
+ class_option :skip_namespace, type: :boolean, default: false,
+ desc: "Skip namespace (affects only isolated applications)"
+
+ def initialize(args, *options) #:nodoc:
+ @inside_template = nil
+ # Unfreeze name in case it's given as a frozen string
+ args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen?
+ super
+ assign_names!(self.name)
+ parse_attributes! if respond_to?(:attributes)
+ end
+
+ # Overrides <tt>Thor::Actions#template</tt> so it can tell if
+ # a template is currently being created.
+ no_tasks do
+ def template(source, *args, &block)
+ inside_template do
+ super
+ end
+ end
+ end
+
+ protected
+ attr_reader :file_name
+
+ # FIXME: We are avoiding to use alias because a bug on thor that make
+ # this method public and add it to the task list.
+ def singular_name
+ file_name
+ end
+
+ # Wrap block with namespace of current application
+ # if namespace exists and is not skipped
+ def module_namespacing(&block)
+ content = capture(&block)
+ content = wrap_with_namespace(content) if namespaced?
+ concat(content)
+ end
+
+ def indent(content, multiplier = 2)
+ spaces = " " * multiplier
+ content.each_line.map {|line| line.blank? ? line : "#{spaces}#{line}" }.join
+ end
+
+ def wrap_with_namespace(content)
+ content = indent(content).chomp
+ "module #{namespace.name}\n#{content}\nend\n"
+ end
+
+ def inside_template
+ @inside_template = true
+ yield
+ ensure
+ @inside_template = false
+ end
+
+ def inside_template?
+ @inside_template
+ end
+
+ def namespace
+ Rails::Generators.namespace
+ end
+
+ def namespaced?
+ !options[:skip_namespace] && namespace
+ end
+
+ def file_path
+ @file_path ||= (class_path + [file_name]).join('/')
+ end
+
+ def class_path
+ inside_template? || !namespaced? ? regular_class_path : namespaced_class_path
+ end
+
+ def regular_class_path
+ @class_path
+ end
+
+ def namespaced_file_path
+ @namespaced_file_path ||= namespaced_class_path.join("/")
+ end
+
+ def namespaced_class_path
+ @namespaced_class_path ||= [namespaced_path] + @class_path
+ end
+
+ def namespaced_path
+ @namespaced_path ||= namespace.name.split("::").first.underscore
+ end
+
+ def class_name
+ (class_path + [file_name]).map!(&:camelize).join('::')
+ end
+
+ def human_name
+ @human_name ||= singular_name.humanize
+ end
+
+ def plural_name
+ @plural_name ||= singular_name.pluralize
+ end
+
+ def i18n_scope
+ @i18n_scope ||= file_path.tr('/', '.')
+ end
+
+ def table_name
+ @table_name ||= begin
+ base = pluralize_table_names? ? plural_name : singular_name
+ (class_path + [base]).join('_')
+ end
+ end
+
+ def uncountable?
+ singular_name == plural_name
+ end
+
+ def index_helper
+ uncountable? ? "#{plural_table_name}_index" : plural_table_name
+ end
+
+ def singular_table_name
+ @singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name)
+ end
+
+ def plural_table_name
+ @plural_table_name ||= (pluralize_table_names? ? table_name : table_name.pluralize)
+ end
+
+ def plural_file_name
+ @plural_file_name ||= file_name.pluralize
+ end
+
+ def fixture_file_name
+ @fixture_file_name ||= (pluralize_table_names? ? plural_file_name : file_name)
+ end
+
+ def route_url
+ @route_url ||= class_path.collect {|dname| "/" + dname }.join + "/" + plural_file_name
+ end
+
+ # Tries to retrieve the application name or simply return application.
+ def application_name
+ if defined?(Rails) && Rails.application
+ Rails.application.class.name.split('::').first.underscore
+ else
+ "application"
+ end
+ end
+
+ def assign_names!(name) #:nodoc:
+ @class_path = name.include?('/') ? name.split('/') : name.split('::')
+ @class_path.map!(&:underscore)
+ @file_name = @class_path.pop
+ end
+
+ # Convert attributes array into GeneratedAttribute objects.
+ def parse_attributes! #:nodoc:
+ self.attributes = (attributes || []).map do |attr|
+ Rails::Generators::GeneratedAttribute.parse(attr)
+ end
+ end
+
+ def attributes_names
+ @attributes_names ||= attributes.each_with_object([]) do |a, names|
+ names << a.column_name
+ names << 'password_confirmation' if a.password_digest?
+ names << "#{a.name}_type" if a.polymorphic?
+ end
+ end
+
+ def pluralize_table_names?
+ !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names
+ end
+
+ def mountable_engine?
+ defined?(ENGINE_ROOT) && namespaced?
+ end
+
+ # Add a class collisions name to be checked on class initialization. You
+ # can supply a hash with a :prefix or :suffix to be tested.
+ #
+ # ==== Examples
+ #
+ # check_class_collision suffix: "Decorator"
+ #
+ # If the generator is invoked with class name Admin, it will check for
+ # the presence of "AdminDecorator".
+ #
+ def self.check_class_collision(options={})
+ define_method :check_class_collision do
+ name = if self.respond_to?(:controller_class_name) # for ScaffoldBase
+ controller_class_name
+ else
+ class_name
+ end
+
+ class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}"
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/app/USAGE b/railties/lib/rails/generators/rails/app/USAGE
new file mode 100644
index 0000000000..691095f33f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/USAGE
@@ -0,0 +1,15 @@
+Description:
+ The 'rails new' command creates a new Rails application with a default
+ directory structure and configuration at the path you specify.
+
+ You can specify extra command-line arguments to be used every time
+ 'rails new' runs in the .railsrc configuration file in your home directory.
+
+ Note that the arguments specified in the .railsrc file don't affect the
+ defaults values shown above in this help message.
+
+Example:
+ rails new ~/Code/Ruby/weblog
+
+ This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
+ See the README in the newly created application to get going.
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
new file mode 100644
index 0000000000..889154494d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -0,0 +1,462 @@
+require 'rails/generators/app_base'
+
+module Rails
+ module ActionMethods # :nodoc:
+ attr_reader :options
+
+ def initialize(generator)
+ @generator = generator
+ @options = generator.options
+ end
+
+ private
+ %w(template copy_file directory empty_directory inside
+ empty_directory_with_keep_file create_file chmod shebang).each do |method|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{method}(*args, &block)
+ @generator.send(:#{method}, *args, &block)
+ end
+ RUBY
+ end
+
+ # TODO: Remove once this is fully in place
+ def method_missing(meth, *args, &block)
+ @generator.send(meth, *args, &block)
+ end
+ end
+
+ # The application builder allows you to override elements of the application
+ # generator without being forced to reverse the operations of the default
+ # generator.
+ #
+ # This allows you to override entire operations, like the creation of the
+ # Gemfile, README, or JavaScript files, without needing to know exactly
+ # what those operations do so you can create another template action.
+ class AppBuilder
+ def rakefile
+ template "Rakefile"
+ end
+
+ def readme
+ copy_file "README.md", "README.md"
+ end
+
+ def gemfile
+ template "Gemfile"
+ end
+
+ def configru
+ template "config.ru"
+ end
+
+ def gitignore
+ template "gitignore", ".gitignore"
+ end
+
+ def app
+ directory 'app'
+
+ keep_file 'app/assets/images'
+ keep_file 'app/mailers'
+ keep_file 'app/models'
+
+ keep_file 'app/controllers/concerns'
+ keep_file 'app/models/concerns'
+ end
+
+ def bin
+ directory "bin" do |content|
+ "#{shebang}\n" + content
+ end
+ chmod "bin", 0755 & ~File.umask, verbose: false
+ end
+
+ def config
+ empty_directory "config"
+
+ inside "config" do
+ template "routes.rb"
+ template "application.rb"
+ template "environment.rb"
+ template "secrets.yml"
+
+ directory "environments"
+ directory "initializers"
+ directory "locales"
+ end
+ end
+
+ def config_when_updating
+ cookie_serializer_config_exist = File.exist?('config/initializers/cookies_serializer.rb')
+ callback_terminator_config_exist = File.exist?('config/initializers/callback_terminator.rb')
+ active_record_belongs_to_required_by_default_config_exist = File.exist?('config/initializers/active_record_belongs_to_required_by_default.rb')
+
+ config
+
+ unless callback_terminator_config_exist
+ remove_file 'config/initializers/callback_terminator.rb'
+ end
+
+ unless cookie_serializer_config_exist
+ gsub_file 'config/initializers/cookies_serializer.rb', /json/, 'marshal'
+ end
+
+ unless active_record_belongs_to_required_by_default_config_exist
+ remove_file 'config/initializers/active_record_belongs_to_required_by_default.rb'
+ end
+ end
+
+ def database_yml
+ template "config/databases/#{options[:database]}.yml", "config/database.yml"
+ end
+
+ def db
+ directory "db"
+ end
+
+ def lib
+ empty_directory 'lib'
+ empty_directory_with_keep_file 'lib/tasks'
+ empty_directory_with_keep_file 'lib/assets'
+ end
+
+ def log
+ empty_directory_with_keep_file 'log'
+ end
+
+ def public_directory
+ directory "public", "public", recursive: false
+ end
+
+ def test
+ empty_directory_with_keep_file 'test/fixtures'
+ empty_directory_with_keep_file 'test/fixtures/files'
+ empty_directory_with_keep_file 'test/controllers'
+ empty_directory_with_keep_file 'test/mailers'
+ empty_directory_with_keep_file 'test/models'
+ empty_directory_with_keep_file 'test/helpers'
+ empty_directory_with_keep_file 'test/integration'
+
+ template 'test/test_helper.rb'
+ end
+
+ def tmp
+ empty_directory_with_keep_file "tmp"
+ empty_directory "tmp/cache"
+ empty_directory "tmp/cache/assets"
+ end
+
+ def vendor
+ vendor_javascripts
+ vendor_stylesheets
+ end
+
+ def vendor_javascripts
+ unless options[:skip_javascript]
+ empty_directory_with_keep_file 'vendor/assets/javascripts'
+ end
+ end
+
+ def vendor_stylesheets
+ empty_directory_with_keep_file 'vendor/assets/stylesheets'
+ end
+ end
+
+ module Generators
+ # We need to store the RAILS_DEV_PATH in a constant, otherwise the path
+ # can change in Ruby 1.8.7 when we FileUtils.cd.
+ RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
+ RESERVED_NAMES = %w[application destroy plugin runner test]
+
+ class AppGenerator < AppBase # :nodoc:
+ add_shared_options_for "application"
+
+ # Add bin/rails options
+ class_option :version, type: :boolean, aliases: "-v", group: :rails,
+ desc: "Show Rails version number and quit"
+
+ class_option :api, type: :boolean,
+ desc: "Preconfigure smaller stack for API only apps"
+
+ def initialize(*args)
+ super
+
+ unless app_path
+ raise Error, "Application name should be provided in arguments. For details run: rails --help"
+ end
+
+ if !options[:skip_active_record] && !DATABASES.include?(options[:database])
+ raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
+ end
+
+ # Force sprockets to be skipped when generating API only apps.
+ # Can't modify options hash as it's frozen by default.
+ self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze if options[:api]
+ end
+
+ public_task :set_default_accessors!
+ public_task :create_root
+
+ def create_root_files
+ build(:readme)
+ build(:rakefile)
+ build(:configru)
+ build(:gitignore) unless options[:skip_git]
+ build(:gemfile) unless options[:skip_gemfile]
+ end
+
+ def create_app_files
+ build(:app)
+ end
+
+ def create_bin_files
+ build(:bin)
+ end
+
+ def create_config_files
+ build(:config)
+ end
+
+ def update_config_files
+ build(:config_when_updating)
+ end
+ remove_task :update_config_files
+
+ def create_boot_file
+ template "config/boot.rb"
+ end
+
+ def create_active_record_files
+ return if options[:skip_active_record]
+ build(:database_yml)
+ end
+
+ def create_db_files
+ build(:db)
+ end
+
+ def create_lib_files
+ build(:lib)
+ end
+
+ def create_log_files
+ build(:log)
+ end
+
+ def create_public_files
+ build(:public_directory)
+ end
+
+ def create_test_files
+ build(:test) unless options[:skip_test]
+ end
+
+ def create_tmp_files
+ build(:tmp)
+ end
+
+ def create_vendor_files
+ build(:vendor)
+ end
+
+ def delete_app_assets_if_api_option
+ if options[:api]
+ remove_dir 'app/assets'
+ remove_dir 'lib/assets'
+ remove_dir 'tmp/cache/assets'
+ remove_dir 'vendor/assets'
+ end
+ end
+
+ def delete_app_helpers_if_api_option
+ if options[:api]
+ remove_dir 'app/helpers'
+ remove_dir 'test/helpers'
+ end
+ end
+
+ def delete_app_views_if_api_option
+ if options[:api]
+ remove_dir 'app/views'
+ end
+ end
+
+ def delete_js_folder_skipping_javascript
+ if options[:skip_javascript]
+ remove_dir 'app/assets/javascripts'
+ end
+ end
+
+ def delete_assets_initializer_skipping_sprockets
+ if options[:skip_sprockets]
+ remove_file 'config/initializers/assets.rb'
+ end
+ end
+
+ def delete_active_record_initializers_skipping_active_record
+ if options[:skip_active_record]
+ remove_file 'config/initializers/active_record_belongs_to_required_by_default.rb'
+ end
+ end
+
+ def delete_non_api_initializers_if_api_option
+ if options[:api]
+ remove_file 'config/initializers/session_store.rb'
+ remove_file 'config/initializers/cookies_serializer.rb'
+ remove_file 'config/initializers/request_forgery_protection.rb'
+ end
+ end
+
+ def finish_template
+ build(:leftovers)
+ end
+
+ public_task :apply_rails_template, :run_bundle
+ public_task :generate_spring_binstubs
+
+ def run_after_bundle_callbacks
+ @after_bundle_callbacks.each(&:call)
+ end
+
+ protected
+
+ def self.banner
+ "rails new #{self.arguments.map(&:usage).join(' ')} [options]"
+ end
+
+ # Define file as an alias to create_file for backwards compatibility.
+ def file(*args, &block)
+ create_file(*args, &block)
+ end
+
+ def app_name
+ @app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr('\\', '').tr(". ", "_")
+ end
+
+ def defined_app_name
+ defined_app_const_base.underscore
+ end
+
+ def defined_app_const_base
+ Rails.respond_to?(:application) && defined?(Rails::Application) &&
+ Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "")
+ end
+
+ alias :defined_app_const_base? :defined_app_const_base
+
+ def app_const_base
+ @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, '_').squeeze('_').camelize
+ end
+ alias :camelized :app_const_base
+
+ def app_const
+ @app_const ||= "#{app_const_base}::Application"
+ end
+
+ def valid_const?
+ if app_const =~ /^\d/
+ raise Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers."
+ elsif RESERVED_NAMES.include?(app_name)
+ raise Error, "Invalid application name #{app_name}. Please give a " \
+ "name which does not match one of the reserved rails " \
+ "words: #{RESERVED_NAMES.join(", ")}"
+ elsif Object.const_defined?(app_const_base)
+ raise Error, "Invalid application name #{app_name}, constant #{app_const_base} is already in use. Please choose another application name."
+ end
+ end
+
+ def app_secret
+ SecureRandom.hex(64)
+ end
+
+ def mysql_socket
+ @mysql_socket ||= [
+ "/tmp/mysql.sock", # default
+ "/var/run/mysqld/mysqld.sock", # debian/gentoo
+ "/var/tmp/mysql.sock", # freebsd
+ "/var/lib/mysql/mysql.sock", # fedora
+ "/opt/local/lib/mysql/mysql.sock", # fedora
+ "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
+ "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
+ "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
+ "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
+ ].find { |f| File.exist?(f) } unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
+ end
+
+ def get_builder_class
+ defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder
+ end
+ end
+
+ # This class handles preparation of the arguments before the AppGenerator is
+ # called. The class provides version or help information if they were
+ # requested, and also constructs the railsrc file (used for extra configuration
+ # options).
+ #
+ # This class should be called before the AppGenerator is required and started
+ # since it configures and mutates ARGV correctly.
+ class ARGVScrubber # :nodoc:
+ def initialize(argv = ARGV)
+ @argv = argv
+ end
+
+ def prepare!
+ handle_version_request!(@argv.first)
+ handle_invalid_command!(@argv.first, @argv) do
+ handle_rails_rc!(@argv.drop(1))
+ end
+ end
+
+ def self.default_rc_file
+ File.expand_path('~/.railsrc')
+ end
+
+ private
+
+ def handle_version_request!(argument)
+ if ['--version', '-v'].include?(argument)
+ require 'rails/version'
+ puts "Rails #{Rails::VERSION::STRING}"
+ exit(0)
+ end
+ end
+
+ def handle_invalid_command!(argument, argv)
+ if argument == "new"
+ yield
+ else
+ ['--help'] + argv.drop(1)
+ end
+ end
+
+ def handle_rails_rc!(argv)
+ if argv.find { |arg| arg == '--no-rc' }
+ argv.reject { |arg| arg == '--no-rc' }
+ else
+ railsrc(argv) { |rc_argv, rc| insert_railsrc_into_argv!(rc_argv, rc) }
+ end
+ end
+
+ def railsrc(argv)
+ if (customrc = argv.index{ |x| x.include?("--rc=") })
+ fname = File.expand_path(argv[customrc].gsub(/--rc=/, ""))
+ yield(argv.take(customrc) + argv.drop(customrc + 1), fname)
+ else
+ yield argv, self.class.default_rc_file
+ end
+ end
+
+ def read_rc_file(railsrc)
+ extra_args = File.readlines(railsrc).flat_map(&:split)
+ puts "Using #{extra_args.join(" ")} from #{railsrc}"
+ extra_args
+ end
+
+ def insert_railsrc_into_argv!(argv, railsrc)
+ return argv unless File.exist?(railsrc)
+ extra_args = read_rc_file railsrc
+ argv.take(1) + extra_args + argv.drop(1)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
new file mode 100644
index 0000000000..2f7ffc055d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -0,0 +1,55 @@
+source 'https://rubygems.org'
+
+<% gemfile_entries.each do |gem| -%>
+<% if gem.comment -%>
+
+# <%= gem.comment %>
+<% end -%>
+<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%>
+<% if gem.options.any? -%>
+, <%= gem.options.map { |k,v|
+ "#{k}: #{v.inspect}" }.join(', ') %>
+<% end -%>
+<% end -%>
+
+# Use ActiveModel has_secure_password
+# gem 'bcrypt', '~> 3.1.7'
+
+# Use Unicorn as the app server
+# gem 'unicorn'
+
+# Use Capistrano for deployment
+# gem 'capistrano-rails', group: :development
+
+<%- if options.api? -%>
+# Use ActiveModelSerializers to serialize JSON responses
+gem 'active_model_serializers', '~> 0.10.0.rc2'
+
+# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
+# gem 'rack-cors'
+
+<%- end -%>
+<% if RUBY_ENGINE == 'ruby' -%>
+group :development, :test do
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
+ gem 'byebug'
+end
+
+group :development do
+<%- unless options.api? -%>
+ # Access an IRB console on exception pages or by using <%%= console %> in views
+ <%- if options.dev? || options.edge? -%>
+ gem 'web-console', github: 'rails/web-console'
+ <%- else -%>
+ gem 'web-console', '~> 3.0'
+ <%- end -%>
+<%- end -%>
+<% if spring_install? -%>
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
+ gem 'spring'
+<% end -%>
+end
+<% end -%>
+
+# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
+gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
diff --git a/railties/lib/rails/generators/rails/app/templates/README.md b/railties/lib/rails/generators/rails/app/templates/README.md
new file mode 100644
index 0000000000..55e144da18
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/README.md
@@ -0,0 +1,24 @@
+## README
+
+This README would normally document whatever steps are necessary to get the
+application up and running.
+
+Things you may want to cover:
+
+* Ruby version
+
+* System dependencies
+
+* Configuration
+
+* Database creation
+
+* Database initialization
+
+* How to run the test suite
+
+* Services (job queues, cache servers, search engines, etc.)
+
+* Deployment instructions
+
+* ...
diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile
new file mode 100644
index 0000000000..ba6b733dd2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/Rakefile
@@ -0,0 +1,6 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+
+Rails.application.load_tasks
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
new file mode 100644
index 0000000000..f80631bac6
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
@@ -0,0 +1,8 @@
+
+<% unless options.api? -%>
+//= link_tree ../images
+<% end -%>
+<% unless options.skip_javascript -%>
+//= link_directory ../javascripts .js
+<% end -%>
+//= link_directory ../stylesheets .css
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
new file mode 100644
index 0000000000..c88426ec06
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
@@ -0,0 +1,20 @@
+// 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, vendor/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
+// compiled file. JavaScript code in this file should be added after the last require_* statement.
+//
+// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
+// about supported directives.
+//
+<% unless options[:skip_javascript] -%>
+//= require <%= options[:javascript] %>
+//= require <%= options[:javascript] %>_ujs
+<% if gemfile_entries.any? { |m| m.name == "turbolinks" } -%>
+//= require turbolinks
+<% end -%>
+<% end -%>
+//= require_tree .
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
new file mode 100644
index 0000000000..0ebd7fe829
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
@@ -0,0 +1,15 @@
+/*
+ * 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, vendor/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
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
+ * files in this directory. Styles in this file should be added after the last require_* statement.
+ * It is generally better to create a new file per style scope.
+ *
+ *= require_tree .
+ *= require_self
+ */
diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
new file mode 100644
index 0000000000..f726fd6305
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
@@ -0,0 +1,7 @@
+class ApplicationController < ActionController::<%= options[:api] ? "API" : "Base" %>
+<%- unless options[:api] -%>
+ # Prevent CSRF attacks by raising an exception.
+ # For APIs, you may want to use :null_session instead.
+ protect_from_forgery with: :exception
+<%- end -%>
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb
new file mode 100644
index 0000000000..de6be7945c
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb
new file mode 100644
index 0000000000..a009ace51c
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb
@@ -0,0 +1,2 @@
+class ApplicationJob < ActiveJob::Base
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
new file mode 100644
index 0000000000..8bb4440f55
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title><%= camelized %></title>
+ <%- if options[:skip_javascript] -%>
+ <%%= stylesheet_link_tag 'application', media: 'all' %>
+ <%- else -%>
+ <%- if gemfile_entries.any? { |m| m.name == 'turbolinks' } -%>
+ <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
+ <%%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
+ <%- else -%>
+ <%%= stylesheet_link_tag 'application', media: 'all' %>
+ <%%= javascript_include_tag 'application' %>
+ <%- end -%>
+ <%- end -%>
+ <%%= csrf_meta_tags %>
+ </head>
+
+ <body>
+
+ <%%= yield %>
+
+ </body>
+</html>
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle b/railties/lib/rails/generators/rails/app/templates/bin/bundle
new file mode 100644
index 0000000000..1123dcf501
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/bundle
@@ -0,0 +1,2 @@
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+load Gem.bin_path('bundler', 'bundle')
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rails b/railties/lib/rails/generators/rails/app/templates/bin/rails
new file mode 100644
index 0000000000..80ec8080ab
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/rails
@@ -0,0 +1,3 @@
+APP_PATH = File.expand_path('../../config/application', __FILE__)
+require_relative '../config/boot'
+require 'rails/commands'
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rake b/railties/lib/rails/generators/rails/app/templates/bin/rake
new file mode 100644
index 0000000000..d14fc8395b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/rake
@@ -0,0 +1,3 @@
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup b/railties/lib/rails/generators/rails/app/templates/bin/setup
new file mode 100644
index 0000000000..0c8b179827
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup
@@ -0,0 +1,33 @@
+require 'pathname'
+require 'fileutils'
+include FileUtils
+
+# path to your application root.
+APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+chdir APP_ROOT do
+ # This script is a starting point to setup your application.
+ # Add necessary setup steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system('bundle check') or system!('bundle install')
+
+ # puts "\n== Copying sample files =="
+ # unless File.exist?('config/database.yml')
+ # cp 'config/database.yml.sample', 'config/database.yml'
+ # end
+
+ puts "\n== Preparing database =="
+ system! 'ruby bin/rake db:setup'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'ruby bin/rake log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'ruby bin/rake restart'
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update b/railties/lib/rails/generators/rails/app/templates/bin/update
new file mode 100644
index 0000000000..9830e6b29a
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/update
@@ -0,0 +1,28 @@
+require 'pathname'
+require 'fileutils'
+include FileUtils
+
+# path to your application root.
+APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+chdir APP_ROOT do
+ # This script is a way to update your development environment automatically.
+ # Add necessary update steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system 'bundle check' or system! 'bundle install'
+
+ puts "\n== Updating database =="
+ system! 'bin/rake db:migrate'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'bin/rake log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'bin/rake restart'
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru
new file mode 100644
index 0000000000..bd83b25412
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Rails.application
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
new file mode 100644
index 0000000000..ddd0fcade1
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -0,0 +1,43 @@
+require File.expand_path('../boot', __FILE__)
+
+<% if include_all_railties? -%>
+require 'rails/all'
+<% else -%>
+require "rails"
+# Pick the frameworks you want:
+require "active_model/railtie"
+require "active_job/railtie"
+<%= comment_if :skip_active_record %>require "active_record/railtie"
+require "action_controller/railtie"
+<%= comment_if :skip_action_mailer %>require "action_mailer/railtie"
+require "action_view/railtie"
+<%= comment_if :skip_sprockets %>require "sprockets/railtie"
+<%= comment_if :skip_test %>require "rails/test_unit/railtie"
+<% end -%>
+
+# Require the gems listed in Gemfile, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(*Rails.groups)
+
+module <%= app_const_base %>
+ class Application < Rails::Application
+ # 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.
+
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+ # Run "rake time:zones:all" for a time zone names list. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+ # config.i18n.default_locale = :de
+<%- if options[:api] -%>
+
+ # Only loads a smaller set of middleware suitable for API only apps.
+ # Middleware like session, flash, cookies can be added back manually.
+ # Skip views, helpers and assets when generating a new resource.
+ config.api_only = true
+<%- end -%>
+ end
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb
new file mode 100644
index 0000000000..6b750f00b1
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb
@@ -0,0 +1,3 @@
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+
+require 'bundler/setup' # Set up gems listed in the Gemfile.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml
new file mode 100644
index 0000000000..34fc0e3465
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml
@@ -0,0 +1,49 @@
+# FrontBase versions 4.x
+#
+# Get the bindings:
+# gem install ruby-frontbase
+#
+# Configure Using Gemfile
+# gem 'ruby-frontbase'
+#
+default: &default
+ adapter: frontbase
+ host: localhost
+ username: <%= app_name %>
+ password: ''
+
+development:
+ <<: *default
+ database: <%= app_name %>_development
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="frontbase://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
new file mode 100644
index 0000000000..187ff01bac
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
@@ -0,0 +1,85 @@
+# IBM Dataservers
+#
+# Home Page
+# https://github.com/dparnell/ibm_db
+#
+# To install the ibm_db gem:
+#
+# On Linux:
+# . /home/db2inst1/sqllib/db2profile
+# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include
+# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32
+# gem install ibm_db
+#
+# On Mac OS X 10.5:
+# . /home/db2inst1/sqllib/db2profile
+# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include
+# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32
+# export ARCHFLAGS="-arch i386"
+# gem install ibm_db
+#
+# On Mac OS X 10.6:
+# . /home/db2inst1/sqllib/db2profile
+# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include
+# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib64
+# export ARCHFLAGS="-arch x86_64"
+# gem install ibm_db
+#
+# On Windows:
+# Issue the command: gem install ibm_db
+#
+# Configure Using Gemfile
+# gem 'ibm_db'
+#
+#
+default: &default
+ adapter: ibm_db
+ username: db2inst1
+ password:
+ #schema: db2inst1
+ #host: localhost
+ #port: 50000
+ #account: my_account
+ #app_user: my_app_user
+ #application: my_application
+ #workstation: my_workstation
+ #security: SSL
+ #timeout: 10
+ #authentication: SERVER
+ #parameterized: false
+
+development:
+ <<: *default
+ database: <%= app_name[0,4] %>_dev
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name[0,4] %>_tst
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="ibm-db://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml
new file mode 100644
index 0000000000..db0a429753
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml
@@ -0,0 +1,68 @@
+# If you are using mssql, derby, hsqldb, or h2 with one of the
+# ActiveRecord JDBC adapters, install the appropriate driver, e.g.,:
+# gem install activerecord-jdbcmssql-adapter
+#
+# Configure using Gemfile:
+# gem 'activerecord-jdbcmssql-adapter'
+#
+# development:
+# adapter: mssql
+# username: <%= app_name %>
+# password:
+# host: localhost
+# database: <%= app_name %>_development
+#
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+#
+# test:
+# adapter: mssql
+# username: <%= app_name %>
+# password:
+# host: localhost
+# database: <%= app_name %>_test
+#
+# production:
+# adapter: mssql
+# username: <%= app_name %>
+# password:
+# host: localhost
+# database: <%= app_name %>_production
+
+# If you are using oracle, db2, sybase, informix or prefer to use the plain
+# JDBC adapter, configure your database setting as the example below (requires
+# you to download and manually install the database vendor's JDBC driver .jar
+# file). See your driver documentation for the appropriate driver class and
+# connection string:
+
+default: &default
+ adapter: jdbc
+ username: <%= app_name %>
+ password:
+ driver:
+
+development:
+ <<: *default
+ url: jdbc:db://localhost/<%= app_name %>_development
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ url: jdbc:db://localhost/<%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+production:
+ url: jdbc:db://localhost/<%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
new file mode 100644
index 0000000000..5ca549a8c8
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml
@@ -0,0 +1,52 @@
+# MySQL. Versions 4.1 and 5.0 are recommended.
+#
+# Install the MySQL driver:
+# gem install activerecord-jdbcmysql-adapter
+#
+# Configure Using Gemfile
+# gem 'activerecord-jdbcmysql-adapter'
+#
+# And be sure to use new-style password hashing:
+# http://dev.mysql.com/doc/refman/5.7/en/old-client.html
+#
+default: &default
+ adapter: mysql
+ username: root
+ password:
+ host: localhost
+
+development:
+ <<: *default
+ database: <%= app_name %>_development
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml
new file mode 100644
index 0000000000..9e99264d33
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml
@@ -0,0 +1,68 @@
+# PostgreSQL. Versions 8.2 and up are supported.
+#
+# Configure Using Gemfile
+# gem 'activerecord-jdbcpostgresql-adapter'
+#
+default: &default
+ adapter: postgresql
+ encoding: unicode
+
+development:
+ <<: *default
+ database: <%= app_name %>_development
+
+ # The specified database role being used to connect to postgres.
+ # To create additional roles in postgres see `$ createuser --help`.
+ # When left blank, postgres will use the default role. This is
+ # the same name as the operating system user that initialized the database.
+ #username: <%= app_name %>
+
+ # The password associated with the postgres role (username).
+ #password:
+
+ # Connect on a TCP socket. Omitted by default since the client uses a
+ # domain socket that doesn't need configuration. Windows does not have
+ # domain sockets, so uncomment these lines.
+ #host: localhost
+ #port: 5432
+
+ # Schema search path. The server defaults to $user,public
+ #schema_search_path: myapp,sharedapp,public
+
+ # Minimum log levels, in increasing order:
+ # debug5, debug4, debug3, debug2, debug1,
+ # log, notice, warning, error, fatal, and panic
+ # Defaults to warning.
+ #min_messages: notice
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml
new file mode 100644
index 0000000000..28c36eb82f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml
@@ -0,0 +1,23 @@
+# SQLite version 3.x
+# gem 'activerecord-jdbcsqlite3-adapter'
+#
+# Configure Using Gemfile
+# gem 'activerecord-jdbcsqlite3-adapter'
+#
+default: &default
+ adapter: sqlite3
+
+development:
+ <<: *default
+ database: db/development.sqlite3
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: db/test.sqlite3
+
+production:
+ <<: *default
+ database: db/production.sqlite3
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
new file mode 100644
index 0000000000..119c2fe2c3
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml
@@ -0,0 +1,58 @@
+# MySQL. Versions 5.0+ are recommended.
+#
+# Install the MySQL driver
+# gem install mysql2
+#
+# Ensure the MySQL gem is defined in your Gemfile
+# gem 'mysql2'
+#
+# And be sure to use new-style password hashing:
+# http://dev.mysql.com/doc/refman/5.7/en/old-client.html
+#
+default: &default
+ adapter: mysql2
+ encoding: utf8
+ pool: 5
+ username: root
+ password:
+<% if mysql_socket -%>
+ socket: <%= mysql_socket %>
+<% else -%>
+ host: localhost
+<% end -%>
+
+development:
+ <<: *default
+ database: <%= app_name %>_development
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
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
new file mode 100644
index 0000000000..9aedcc15cb
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
@@ -0,0 +1,58 @@
+# Oracle/OCI 8i, 9, 10g
+#
+# Requires Ruby/OCI8:
+# https://github.com/kubo/ruby-oci8
+#
+# Specify your database using any valid connection syntax, such as a
+# tnsnames.ora service name, or an SQL connect string of the form:
+#
+# //host:[port][/service name]
+#
+# By default prefetch_rows (OCI_ATTR_PREFETCH_ROWS) is set to 100. And
+# until true bind variables are supported, cursor_sharing is set by default
+# to 'similar'. Both can be changed in the configuration below; the defaults
+# are equivalent to specifying:
+#
+# prefetch_rows: 100
+# cursor_sharing: similar
+#
+default: &default
+ adapter: oracle
+ username: <%= app_name %>
+ password:
+
+development:
+ <<: *default
+ database: <%= app_name %>_development
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# 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"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
new file mode 100644
index 0000000000..feb25bbc6b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
@@ -0,0 +1,85 @@
+# PostgreSQL. Versions 8.2 and up are supported.
+#
+# Install the pg driver:
+# gem install pg
+# On OS X with Homebrew:
+# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
+# On OS X with MacPorts:
+# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
+# On Windows:
+# gem install pg
+# Choose the win32 build.
+# Install PostgreSQL and put its /bin directory on your path.
+#
+# Configure Using Gemfile
+# gem 'pg'
+#
+default: &default
+ adapter: postgresql
+ encoding: unicode
+ # For details on connection pooling, see rails configuration guide
+ # http://guides.rubyonrails.org/configuring.html#database-pooling
+ pool: 5
+
+development:
+ <<: *default
+ database: <%= app_name %>_development
+
+ # The specified database role being used to connect to postgres.
+ # To create additional roles in postgres see `$ createuser --help`.
+ # When left blank, postgres will use the default role. This is
+ # the same name as the operating system user that initialized the database.
+ #username: <%= app_name %>
+
+ # The password associated with the postgres role (username).
+ #password:
+
+ # Connect on a TCP socket. Omitted by default since the client uses a
+ # domain socket that doesn't need configuration. Windows does not have
+ # domain sockets, so uncomment these lines.
+ #host: localhost
+
+ # The TCP port the server listens on. Defaults to 5432.
+ # If your server runs on a different port number, change accordingly.
+ #port: 5432
+
+ # Schema search path. The server defaults to $user,public
+ #schema_search_path: myapp,sharedapp,public
+
+ # Minimum log levels, in increasing order:
+ # debug5, debug4, debug3, debug2, debug1,
+ # log, notice, warning, error, fatal, and panic
+ # Defaults to warning.
+ #min_messages: notice
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
new file mode 100644
index 0000000000..1c1a37ca8d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+#
+default: &default
+ adapter: sqlite3
+ pool: 5
+ timeout: 5000
+
+development:
+ <<: *default
+ database: db/development.sqlite3
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: db/test.sqlite3
+
+production:
+ <<: *default
+ database: db/production.sqlite3
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
new file mode 100644
index 0000000000..30b0df34a8
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
@@ -0,0 +1,68 @@
+# SQL Server (2005 or higher recommended)
+#
+# Install the adapters and driver
+# gem install tiny_tds
+# gem install activerecord-sqlserver-adapter
+#
+# Ensure the activerecord adapter and db driver gems are defined in your Gemfile
+# gem 'tiny_tds'
+# gem 'activerecord-sqlserver-adapter'
+#
+# You should make sure freetds is configured correctly first.
+# freetds.conf contains host/port/protocol_versions settings.
+# http://freetds.schemamania.org/userguide/freetdsconf.htm
+#
+# A typical Microsoft server
+# [mssql]
+# host = mssqlserver.yourdomain.com
+# port = 1433
+# tds version = 7.1
+
+# If you can connect with "tsql -S servername", your basic FreeTDS installation is working.
+# 'man tsql' for more info
+# Set timeout to a larger number if valid queries against a live db fail
+#
+default: &default
+ adapter: sqlserver
+ encoding: utf8
+ reconnect: false
+ username: <%= app_name %>
+ password:
+ timeout: 25
+ dataserver: from_freetds.conf
+
+development:
+ <<: *default
+ database: <%= app_name %>_development
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: <%= app_name %>_test
+
+# As with config/secrets.yml, you never want to store sensitive information,
+# like your database password, in your source code. If your source code is
+# ever seen by anyone, they now have access to your database.
+#
+# Instead, provide the password as a unix environment variable when you boot
+# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
+# for a full rundown on how to provide these environment variables in a
+# production deployment.
+#
+# On Heroku and other platform providers, you may have a full connection URL
+# available as an environment variable. For example:
+#
+# DATABASE_URL="sqlserver://myuser:mypass@localhost/somedatabase"
+#
+# You can use this database configuration with:
+#
+# production:
+# url: <%%= ENV['DATABASE_URL'] %>
+#
+production:
+ <<: *default
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb
new file mode 100644
index 0000000000..ee8d90dc65
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the Rails application.
+require File.expand_path('../application', __FILE__)
+
+# Initialize the Rails application.
+Rails.application.initialize!
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
new file mode 100644
index 0000000000..65c6aeb694
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -0,0 +1,62 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports.
+ config.consider_all_requests_local = true
+
+ # Enable/disable caching. By default caching is disabled.
+ if Rails.root.join('tmp/caching-dev.txt').exist?
+ config.action_controller.perform_caching = true
+ config.cache_store = :memory_store
+ config.public_file_server.headers = {
+ 'Cache-Control' => 'public, max-age=172800'
+ }
+ else
+ config.action_controller.perform_caching = false
+ config.cache_store = :null_store
+ end
+ <%- unless options.skip_action_mailer? -%>
+
+ # Don't care if the mailer can't send.
+ config.action_mailer.raise_delivery_errors = false
+ <%- end -%>
+
+ # Print deprecation notices to the Rails logger.
+ config.active_support.deprecation = :log
+
+ <%- unless options.skip_active_record? -%>
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ <%- end -%>
+ <%- unless options.skip_sprockets? -%>
+ # Debug mode disables concatenation and preprocessing of assets.
+ # This option may cause significant delays in view rendering with a large
+ # number of complex assets.
+ config.assets.debug = true
+
+ # Asset digests allow you to set far-future HTTP expiration dates on all assets,
+ # yet still be able to expire them through the digest params.
+ config.assets.digest = true
+
+ # Adds additional error checking when serving assets at runtime.
+ # Checks for improperly declared sprockets dependencies.
+ # Raises helpful error messages.
+ config.assets.raise_runtime_errors = true
+ <%- end -%>
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+
+ # Use an evented file watcher to asynchronously detect changes in source code,
+ # routes, locales, etc. This feature depends on the listen gem.
+ # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
new file mode 100644
index 0000000000..a5302550fa
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -0,0 +1,84 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
+ config.cache_classes = true
+
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both threaded web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
+
+ <%- unless options.skip_sprockets? -%>
+ # Compress JavaScripts and CSS.
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
+
+ # Do not fallback to assets pipeline if a precompiled asset is missed.
+ config.assets.compile = false
+
+ # Asset digests allow you to set far-future HTTP expiration dates on all assets,
+ # yet still be able to expire them through the digest params.
+ config.assets.digest = true
+
+ # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
+ <%- end -%>
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # Use the lowest log level to ensure availability of diagnostic information
+ # when problems arise.
+ config.log_level = :debug
+
+ # Prepend all log lines with the following tags.
+ # config.log_tags = [ :subdomain, :request_id ]
+
+ # Use a different logger for distributed setups.
+ # require 'syslog/logger'
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+
+ # Use a different cache store in production.
+ # config.cache_store = :mem_cache_store
+
+ # Use a real queuing backend for Active Job (and separate queues per environment)
+ # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_name_prefix = "<%= app_name %>_#{Rails.env}"
+ <%- unless options.skip_action_mailer? -%>
+
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
+ # config.action_mailer.raise_delivery_errors = false
+ <%- end -%>
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation cannot be found).
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners.
+ config.active_support.deprecation = :notify
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+ <%- unless options.skip_active_record? -%>
+
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
+ <%- end -%>
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
new file mode 100644
index 0000000000..8133917591
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -0,0 +1,46 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ 'Cache-Control' => 'public, max-age=3600'
+ }
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates.
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
+ <%- unless options.skip_action_mailer? -%>
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+ <%- end -%>
+
+ # Randomize the order test cases are executed.
+ config.active_support.test_order = :random
+
+ # Print deprecation notices to the stderr.
+ config.active_support.deprecation = :stderr
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/active_record_belongs_to_required_by_default.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/active_record_belongs_to_required_by_default.rb
new file mode 100644
index 0000000000..30c4f89792
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/active_record_belongs_to_required_by_default.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Require `belongs_to` associations by default.
+Rails.application.config.active_record.belongs_to_required_by_default = true
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb
new file mode 100644
index 0000000000..ea930f54da
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb
@@ -0,0 +1,6 @@
+## Change renderer defaults here.
+#
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
new file mode 100644
index 0000000000..01ef3e6630
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
@@ -0,0 +1,11 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = '1.0'
+
+# Add additional assets to the asset load path
+# Rails.application.config.assets.paths << Emoji.images_path
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
+# Rails.application.config.assets.precompile += %w( search.js )
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
new file mode 100644
index 0000000000..59385cdf37
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/callback_terminator.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/callback_terminator.rb
new file mode 100644
index 0000000000..a70a1b9cde
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/callback_terminator.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Do not halt callback chains when a callback returns false.
+ActiveSupport.halt_callback_chains_on_return_false = false
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb
new file mode 100644
index 0000000000..7f70458dee
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb
@@ -0,0 +1,3 @@
+# Be sure to restart your server when you modify this file.
+
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
new file mode 100644
index 0000000000..3b1c1b5ed1
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# Avoid CORS issues when API is called from the frontend app.
+# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
+
+# Read more: https://github.com/cyu/rack-cors
+
+# Rails.application.config.middleware.insert_before 0, Rack::Cors do
+# allow do
+# origins 'example.com'
+#
+# resource '*',
+# headers: :any,
+# methods: [:get, :post, :put, :patch, :delete, :options, :head]
+# end
+# end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000000..4a994e1e7b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure sensitive parameters which will be filtered from the log file.
+Rails.application.config.filter_parameters += [:password]
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
new file mode 100644
index 0000000000..ac033bf9dc
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb
new file mode 100644
index 0000000000..dc1899682b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/request_forgery_protection.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/request_forgery_protection.rb
new file mode 100644
index 0000000000..3eab78a885
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/request_forgery_protection.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Enable origin-checking CSRF mitigation.
+Rails.application.config.action_controller.forgery_protection_origin_check = true
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
new file mode 100644
index 0000000000..2bb9b82c61
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
@@ -0,0 +1,3 @@
+# Be sure to restart your server when you modify this file.
+
+Rails.application.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
new file mode 100644
index 0000000000..cadc85cfac
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
+<%- unless options.skip_active_record? -%>
+
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
+<%- end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
new file mode 100644
index 0000000000..0653957166
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
@@ -0,0 +1,23 @@
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# To learn more, please read the Rails Internationalization guide
+# available at http://guides.rubyonrails.org/i18n.html.
+
+en:
+ hello: "Hello world"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
new file mode 100644
index 0000000000..787824f888
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -0,0 +1,3 @@
+Rails.application.routes.draw do
+ # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
+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
new file mode 100644
index 0000000000..b2669a0f79
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml
@@ -0,0 +1,22 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key is used for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+# You can use `rake secret` to generate a secure secret key.
+
+# Make sure the secrets in this file are kept private
+# if you're sharing your code publicly.
+
+development:
+ secret_key_base: <%= app_secret %>
+
+test:
+ secret_key_base: <%= app_secret %>
+
+# Do not keep production secrets in the repository,
+# instead read values from the environment.
+production:
+ secret_key_base: <%%= ENV["SECRET_KEY_BASE"] %>
diff --git a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt
new file mode 100644
index 0000000000..4edb1e857e
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt
@@ -0,0 +1,7 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
+# Mayor.create(name: 'Emanuel', city: cities.first)
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore
new file mode 100644
index 0000000000..1b8cf8a9fa
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/gitignore
@@ -0,0 +1,22 @@
+# See https://help.github.com/articles/ignoring-files for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile '~/.gitignore_global'
+
+# Ignore bundler config.
+/.bundle
+
+<% if sqlite3? -%>
+# Ignore the default SQLite database.
+/db/*.sqlite3
+/db/*.sqlite3-journal
+
+<% end -%>
+# Ignore all logfiles and tempfiles.
+/log/*
+/tmp/*
+<% if keeps? -%>
+!/log/.keep
+!/tmp/.keep
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/public/404.html b/railties/lib/rails/generators/rails/app/templates/public/404.html
new file mode 100644
index 0000000000..b612547fc2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/public/404.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The page you were looking for doesn't exist (404)</title>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <style>
+ body {
+ background-color: #EFEFEF;
+ color: #2E2F30;
+ text-align: center;
+ font-family: arial, sans-serif;
+ margin: 0;
+ }
+
+ div.dialog {
+ width: 95%;
+ max-width: 33em;
+ margin: 4em auto 0;
+ }
+
+ div.dialog > div {
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #BBB;
+ border-top: #B00100 solid 4px;
+ border-top-left-radius: 9px;
+ border-top-right-radius: 9px;
+ background-color: white;
+ padding: 7px 12% 0;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+
+ h1 {
+ font-size: 100%;
+ color: #730E15;
+ line-height: 1.5em;
+ }
+
+ div.dialog > p {
+ margin: 0 0 1em;
+ padding: 1em;
+ background-color: #F7F7F7;
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #999;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-top-color: #DADADA;
+ color: #666;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/404.html -->
+ <div class="dialog">
+ <div>
+ <h1>The page you were looking for doesn't exist.</h1>
+ <p>You may have mistyped the address or the page may have moved.</p>
+ </div>
+ <p>If you are the application owner check the logs for more information.</p>
+ </div>
+</body>
+</html>
diff --git a/railties/lib/rails/generators/rails/app/templates/public/422.html b/railties/lib/rails/generators/rails/app/templates/public/422.html
new file mode 100644
index 0000000000..a21f82b3bd
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/public/422.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The change you wanted was rejected (422)</title>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <style>
+ body {
+ background-color: #EFEFEF;
+ color: #2E2F30;
+ text-align: center;
+ font-family: arial, sans-serif;
+ margin: 0;
+ }
+
+ div.dialog {
+ width: 95%;
+ max-width: 33em;
+ margin: 4em auto 0;
+ }
+
+ div.dialog > div {
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #BBB;
+ border-top: #B00100 solid 4px;
+ border-top-left-radius: 9px;
+ border-top-right-radius: 9px;
+ background-color: white;
+ padding: 7px 12% 0;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+
+ h1 {
+ font-size: 100%;
+ color: #730E15;
+ line-height: 1.5em;
+ }
+
+ div.dialog > p {
+ margin: 0 0 1em;
+ padding: 1em;
+ background-color: #F7F7F7;
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #999;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-top-color: #DADADA;
+ color: #666;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/422.html -->
+ <div class="dialog">
+ <div>
+ <h1>The change you wanted was rejected.</h1>
+ <p>Maybe you tried to change something you didn't have access to.</p>
+ </div>
+ <p>If you are the application owner check the logs for more information.</p>
+ </div>
+</body>
+</html>
diff --git a/railties/lib/rails/generators/rails/app/templates/public/500.html b/railties/lib/rails/generators/rails/app/templates/public/500.html
new file mode 100644
index 0000000000..061abc587d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/public/500.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>We're sorry, but something went wrong (500)</title>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <style>
+ body {
+ background-color: #EFEFEF;
+ color: #2E2F30;
+ text-align: center;
+ font-family: arial, sans-serif;
+ margin: 0;
+ }
+
+ div.dialog {
+ width: 95%;
+ max-width: 33em;
+ margin: 4em auto 0;
+ }
+
+ div.dialog > div {
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #BBB;
+ border-top: #B00100 solid 4px;
+ border-top-left-radius: 9px;
+ border-top-right-radius: 9px;
+ background-color: white;
+ padding: 7px 12% 0;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+
+ h1 {
+ font-size: 100%;
+ color: #730E15;
+ line-height: 1.5em;
+ }
+
+ div.dialog > p {
+ margin: 0 0 1em;
+ padding: 1em;
+ background-color: #F7F7F7;
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #999;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-top-color: #DADADA;
+ color: #666;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/500.html -->
+ <div class="dialog">
+ <div>
+ <h1>We're sorry, but something went wrong.</h1>
+ </div>
+ <p>If you are the application owner check the logs for more information.</p>
+ </div>
+</body>
+</html>
diff --git a/railties/lib/rails/generators/rails/app/templates/public/favicon.ico b/railties/lib/rails/generators/rails/app/templates/public/favicon.ico
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/public/favicon.ico
diff --git a/railties/lib/rails/generators/rails/app/templates/public/robots.txt b/railties/lib/rails/generators/rails/app/templates/public/robots.txt
new file mode 100644
index 0000000000..3c9c7c01f3
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/public/robots.txt
@@ -0,0 +1,5 @@
+# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
+#
+# To ban all spiders from the entire site uncomment the next two lines:
+# User-agent: *
+# Disallow: /
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
new file mode 100644
index 0000000000..87b8fe3516
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
@@ -0,0 +1,12 @@
+ENV['RAILS_ENV'] ||= 'test'
+require File.expand_path('../../config/environment', __FILE__)
+require 'rails/test_help'
+
+class ActiveSupport::TestCase
+<% unless options[:skip_active_record] -%>
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
+ fixtures :all
+
+<% end -%>
+ # Add more helper methods to be used by all tests here...
+end
diff --git a/railties/lib/rails/generators/rails/assets/USAGE b/railties/lib/rails/generators/rails/assets/USAGE
new file mode 100644
index 0000000000..d2e5ed4482
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/USAGE
@@ -0,0 +1,20 @@
+Description:
+ Stubs out new asset placeholders. Pass the asset name, either CamelCased
+ or under_scored.
+
+ To create an asset within a folder, specify the asset's name as a
+ path like 'parent/name'.
+
+ This generates a JavaScript stub in app/assets/javascripts and a stylesheet
+ stub in app/assets/stylesheets.
+
+ If CoffeeScript is available, JavaScripts will be generated with the .coffee extension.
+ If Sass 3 is available, stylesheets will be generated with the .scss extension.
+
+Example:
+ `rails generate assets posts`
+
+ Posts assets.
+ JavaScript: app/assets/javascripts/posts.js
+ Stylesheet: app/assets/stylesheets/posts.css
+
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
new file mode 100644
index 0000000000..6f4b86e708
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -0,0 +1,25 @@
+module Rails
+ module Generators
+ class AssetsGenerator < NamedBase # :nodoc:
+ class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
+ class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
+
+ class_option :javascript_engine, desc: "Engine for JavaScripts"
+ class_option :stylesheet_engine, desc: "Engine for Stylesheets"
+
+ protected
+
+ def asset_name
+ file_name
+ end
+
+ hook_for :javascript_engine do |javascript_engine|
+ invoke javascript_engine, [name] if options[:javascripts]
+ end
+
+ hook_for :stylesheet_engine do |stylesheet_engine|
+ invoke stylesheet_engine, [name] if options[:stylesheets]
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/assets/templates/javascript.js b/railties/lib/rails/generators/rails/assets/templates/javascript.js
new file mode 100644
index 0000000000..dee720facd
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/templates/javascript.js
@@ -0,0 +1,2 @@
+// Place all the behaviors and hooks related to the matching controller here.
+// All this logic will automatically be available in application.js.
diff --git a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css
new file mode 100644
index 0000000000..7594abf268
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css
@@ -0,0 +1,4 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+*/
diff --git a/railties/lib/rails/generators/rails/controller/USAGE b/railties/lib/rails/generators/rails/controller/USAGE
new file mode 100644
index 0000000000..64239ad599
--- /dev/null
+++ b/railties/lib/rails/generators/rails/controller/USAGE
@@ -0,0 +1,18 @@
+Description:
+ Stubs out a new controller and its views. Pass the controller name, either
+ CamelCased or under_scored, and a list of views as arguments.
+
+ To create a controller within a module, specify the controller name as a
+ path like 'parent_module/controller_name'.
+
+ This generates a controller class in app/controllers and invokes helper,
+ template engine, assets, and test framework generators.
+
+Example:
+ `rails generate controller CreditCards open debit credit close`
+
+ CreditCards controller with URLs like /credit_cards/debit.
+ Controller: app/controllers/credit_cards_controller.rb
+ Test: test/controllers/credit_cards_controller_test.rb
+ Views: app/views/credit_cards/debit.html.erb [...]
+ Helper: app/helpers/credit_cards_helper.rb
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
new file mode 100644
index 0000000000..0a4c509a31
--- /dev/null
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -0,0 +1,60 @@
+module Rails
+ module Generators
+ class ControllerGenerator < NamedBase # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
+ class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
+
+ check_class_collision suffix: "Controller"
+
+ def create_controller_files
+ template 'controller.rb', File.join('app/controllers', class_path, "#{file_name}_controller.rb")
+ end
+
+ def add_routes
+ unless options[:skip_routes]
+ actions.reverse_each do |action|
+ # route prepends two spaces onto the front of the string that is passed, this corrects that.
+ route generate_routing_code(action)[2..-1]
+ end
+ end
+ end
+
+ hook_for :template_engine, :test_framework
+ hook_for :helper, :assets, hide: true
+
+ private
+
+ # This method creates nested route entry for namespaced resources.
+ # For eg. rails g controller foo/bar/baz index
+ # Will generate -
+ # namespace :foo do
+ # namespace :bar do
+ # get 'baz/index'
+ # end
+ # end
+ def generate_routing_code(action)
+ depth = regular_class_path.length
+ # Create 'namespace' ladder
+ # namespace :foo do
+ # namespace :bar do
+ namespace_ladder = regular_class_path.each_with_index.map do |ns, i|
+ indent(" namespace :#{ns} do\n", i * 2)
+ end.join
+
+ # Create route
+ # get 'baz/index'
+ route = indent(%{ get '#{file_name}/#{action}'\n}, depth * 2)
+
+ # Create `end` ladder
+ # end
+ # end
+ end_ladder = (1..depth).reverse_each.map do |i|
+ indent("end\n", i * 2)
+ end.join
+
+ # Combine the 3 parts to generate complete route entry
+ namespace_ladder + route + end_ladder
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb
new file mode 100644
index 0000000000..633e0b3177
--- /dev/null
+++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb
@@ -0,0 +1,13 @@
+<% if namespaced? -%>
+require_dependency "<%= namespaced_path %>/application_controller"
+
+<% end -%>
+<% module_namespacing do -%>
+class <%= class_name %>Controller < ApplicationController
+<% actions.each do |action| -%>
+ def <%= action %>
+ end
+<%= "\n" unless action == actions.last -%>
+<% end -%>
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/generator/USAGE b/railties/lib/rails/generators/rails/generator/USAGE
new file mode 100644
index 0000000000..799383050c
--- /dev/null
+++ b/railties/lib/rails/generators/rails/generator/USAGE
@@ -0,0 +1,13 @@
+Description:
+ Stubs out a new generator at lib/generators. Pass the generator name as an argument,
+ either CamelCased or snake_cased.
+
+Example:
+ `rails generate generator Awesome`
+
+ creates a standard awesome generator:
+ lib/generators/awesome/
+ lib/generators/awesome/awesome_generator.rb
+ lib/generators/awesome/USAGE
+ lib/generators/awesome/templates/
+ test/lib/generators/awesome_generator_test.rb
diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb
new file mode 100644
index 0000000000..15d88f06ac
--- /dev/null
+++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb
@@ -0,0 +1,27 @@
+module Rails
+ module Generators
+ class GeneratorGenerator < NamedBase # :nodoc:
+ check_class_collision suffix: "Generator"
+
+ class_option :namespace, type: :boolean, default: true,
+ desc: "Namespace generator under lib/generators/name"
+
+ def create_generator_files
+ directory '.', generator_dir
+ end
+
+ hook_for :test_framework
+
+ protected
+
+ def generator_dir
+ if options[:namespace]
+ File.join("lib", "generators", regular_class_path, file_name)
+ else
+ File.join("lib", "generators", regular_class_path)
+ end
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt
new file mode 100644
index 0000000000..d0575772bc
--- /dev/null
+++ b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt
@@ -0,0 +1,3 @@
+class <%= class_name %>Generator < Rails::Generators::NamedBase
+ source_root File.expand_path('../templates', __FILE__)
+end
diff --git a/railties/lib/rails/generators/rails/generator/templates/USAGE.tt b/railties/lib/rails/generators/rails/generator/templates/USAGE.tt
new file mode 100644
index 0000000000..1bb8df840d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/generator/templates/USAGE.tt
@@ -0,0 +1,8 @@
+Description:
+ Explain the generator
+
+Example:
+ rails generate <%= file_name %> Thing
+
+ This will create:
+ what/will/it/create
diff --git a/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory b/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory
diff --git a/railties/lib/rails/generators/rails/helper/USAGE b/railties/lib/rails/generators/rails/helper/USAGE
new file mode 100644
index 0000000000..8855ef3b01
--- /dev/null
+++ b/railties/lib/rails/generators/rails/helper/USAGE
@@ -0,0 +1,13 @@
+Description:
+ Stubs out a new helper. Pass the helper name, either CamelCased
+ or under_scored.
+
+ To create a helper within a module, specify the helper name as a
+ path like 'parent_module/helper_name'.
+
+Example:
+ `rails generate helper CreditCard`
+
+ Credit card helper.
+ Helper: app/helpers/credit_card_helper.rb
+
diff --git a/railties/lib/rails/generators/rails/helper/helper_generator.rb b/railties/lib/rails/generators/rails/helper/helper_generator.rb
new file mode 100644
index 0000000000..5ff38e4111
--- /dev/null
+++ b/railties/lib/rails/generators/rails/helper/helper_generator.rb
@@ -0,0 +1,13 @@
+module Rails
+ module Generators
+ class HelperGenerator < NamedBase # :nodoc:
+ check_class_collision suffix: "Helper"
+
+ def create_helper_files
+ template 'helper.rb', File.join('app/helpers', class_path, "#{file_name}_helper.rb")
+ end
+
+ hook_for :test_framework
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/helper/templates/helper.rb b/railties/lib/rails/generators/rails/helper/templates/helper.rb
new file mode 100644
index 0000000000..b4173151b4
--- /dev/null
+++ b/railties/lib/rails/generators/rails/helper/templates/helper.rb
@@ -0,0 +1,4 @@
+<% module_namespacing do -%>
+module <%= class_name %>Helper
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/integration_test/USAGE b/railties/lib/rails/generators/rails/integration_test/USAGE
new file mode 100644
index 0000000000..57ee3543e6
--- /dev/null
+++ b/railties/lib/rails/generators/rails/integration_test/USAGE
@@ -0,0 +1,10 @@
+Description:
+ Stubs out a new integration test. Pass the name of the test, either
+ CamelCased or under_scored, as an argument.
+
+ This generator invokes the current integration tool, which defaults to
+ TestUnit.
+
+Example:
+ `rails generate integration_test GeneralStories` creates a GeneralStories
+ integration test in test/integration/general_stories_test.rb
diff --git a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
new file mode 100644
index 0000000000..70770ddcb8
--- /dev/null
+++ b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
@@ -0,0 +1,7 @@
+module Rails
+ module Generators
+ class IntegrationTestGenerator < NamedBase # :nodoc:
+ hook_for :integration_tool, as: :integration
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/migration/USAGE b/railties/lib/rails/generators/rails/migration/USAGE
new file mode 100644
index 0000000000..baf3d9894f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/migration/USAGE
@@ -0,0 +1,35 @@
+Description:
+ Stubs out a new database migration. Pass the migration name, either
+ CamelCased or under_scored, and an optional list of attribute pairs as arguments.
+
+ A migration class is generated in db/migrate prefixed by a timestamp of the current date and time.
+
+ You can name your migration in either of these formats to generate add/remove
+ column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable
+
+Example:
+ `rails generate migration AddSslFlag`
+
+ If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration
+ db/migrate/20080514090912_add_ssl_flag.rb
+
+ `rails generate migration AddTitleBodyToPost title:string body:text published:boolean`
+
+ This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with this in the Change migration:
+
+ add_column :posts, :title, :string
+ add_column :posts, :body, :text
+ add_column :posts, :published, :boolean
+
+Migration names containing JoinTable will generate join tables for use with
+has_and_belongs_to_many associations.
+
+Example:
+ `rails g migration CreateMediaJoinTable artists musics:uniq`
+
+ will create the migration
+
+ create_join_table :artists, :musics do |t|
+ # t.index [:artist_id, :music_id]
+ t.index [:music_id, :artist_id], unique: true
+ end
diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb
new file mode 100644
index 0000000000..fca2a8fef4
--- /dev/null
+++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb
@@ -0,0 +1,8 @@
+module Rails
+ module Generators
+ class MigrationGenerator < NamedBase # :nodoc:
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
+ hook_for :orm, required: true, desc: "ORM to be invoked"
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE
new file mode 100644
index 0000000000..11daa5c3cb
--- /dev/null
+++ b/railties/lib/rails/generators/rails/model/USAGE
@@ -0,0 +1,114 @@
+Description:
+ Stubs out a new model. Pass the model name, either CamelCased or
+ under_scored, and an optional list of attribute pairs as arguments.
+
+ Attribute pairs are field:type arguments specifying the
+ model's attributes. Timestamps are added by default, so you don't have to
+ specify them by hand as 'created_at:datetime updated_at:datetime'.
+
+ As a special case, specifying 'password:digest' will generate a
+ password_digest field of string type, and configure your generated model and
+ tests for use with ActiveModel has_secure_password (assuming the default ORM
+ and test framework are being used).
+
+ You don't have to think up every attribute up front, but it helps to
+ sketch out a few so you can start working with the model immediately.
+
+ This generator invokes your configured ORM and test framework, which
+ defaults to ActiveRecord and TestUnit.
+
+ Finally, if --parent option is given, it's used as superclass of the
+ created model. This allows you create Single Table Inheritance models.
+
+ If you pass a namespaced model name (e.g. admin/account or Admin::Account)
+ then the generator will create a module with a table_name_prefix method
+ to prefix the model's table name with the module name (e.g. admin_accounts)
+
+Available field types:
+
+ Just after the field name you can specify a type like text or boolean.
+ It will generate the column with the associated SQL type. For instance:
+
+ `rails generate model post title:string body:text`
+
+ will generate a title column with a varchar type and a body column with a text
+ type. If no type is specified the string type will be used by default.
+ You can use the following types:
+
+ integer
+ primary_key
+ decimal
+ float
+ boolean
+ binary
+ string
+ text
+ date
+ time
+ datetime
+
+ You can also consider `references` as a kind of type. For instance, if you run:
+
+ `rails generate model photo title:string album:references`
+
+ It will generate an `album_id` column. You should generate these kinds of fields when
+ you will use a `belongs_to` association, for instance. `references` also supports
+ polymorphism, you can enable polymorphism like this:
+
+ `rails generate model product supplier:references{polymorphic}`
+
+ For integer, string, text and binary fields, an integer in curly braces will
+ be set as the limit:
+
+ `rails generate model user pseudo:string{30}`
+
+ For decimal, two integers separated by a comma in curly braces will be used
+ for precision and scale:
+
+ `rails generate model product 'price:decimal{10,2}'`
+
+ You can add a `:uniq` or `:index` suffix for unique or standard indexes
+ respectively:
+
+ `rails generate model user pseudo:string:uniq`
+ `rails generate model user pseudo:string:index`
+
+ You can combine any single curly brace option with the index options:
+
+ `rails generate model user username:string{30}:uniq`
+ `rails generate model product supplier:references{polymorphic}:index`
+
+ If you require a `password_digest` string column for use with
+ has_secure_password, you can specify `password:digest`:
+
+ `rails generate model user password:digest`
+
+ If you require a `token` string column for use with
+ has_secure_token, you can specify `auth_token:token`:
+
+ `rails generate model user auth_token:token`
+
+Examples:
+ `rails generate model account`
+
+ For ActiveRecord and TestUnit it creates:
+
+ Model: app/models/account.rb
+ Test: test/models/account_test.rb
+ Fixtures: test/fixtures/accounts.yml
+ Migration: db/migrate/XXX_create_accounts.rb
+
+ `rails generate model post title:string body:text published:boolean`
+
+ Creates a Post model with a string title, text body, and published flag.
+
+ `rails generate model admin/account`
+
+ For ActiveRecord and TestUnit it creates:
+
+ Module: app/models/admin.rb
+ Model: app/models/admin/account.rb
+ Test: test/models/admin/account_test.rb
+ Fixtures: test/fixtures/admin/accounts.yml
+ Migration: db/migrate/XXX_create_admin_accounts.rb
+
diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb
new file mode 100644
index 0000000000..ec78fd855d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/model/model_generator.rb
@@ -0,0 +1,12 @@
+require 'rails/generators/model_helpers'
+
+module Rails
+ module Generators
+ class ModelGenerator < NamedBase # :nodoc:
+ include Rails::Generators::ModelHelpers
+
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
+ hook_for :orm, required: true, desc: "ORM to be invoked"
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/plugin/USAGE b/railties/lib/rails/generators/rails/plugin/USAGE
new file mode 100644
index 0000000000..9a7bf9f396
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/USAGE
@@ -0,0 +1,10 @@
+Description:
+ The 'rails plugin new' command creates a skeleton for developing any
+ kind of Rails extension with ability to run tests using dummy Rails
+ application.
+
+Example:
+ rails plugin new ~/Code/Ruby/blog
+
+ This generates a skeletal Rails plugin in ~/Code/Ruby/blog.
+ See the README in the newly created plugin to get going.
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
new file mode 100644
index 0000000000..776019a6a0
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -0,0 +1,446 @@
+require 'active_support/core_ext/hash/slice'
+require "rails/generators/rails/app/app_generator"
+require 'date'
+
+module Rails
+ # The plugin builder allows you to override elements of the plugin
+ # generator without being forced to reverse the operations of the default
+ # generator.
+ #
+ # This allows you to override entire operations, like the creation of the
+ # Gemfile, \README, or JavaScript files, without needing to know exactly
+ # what those operations do so you can create another template action.
+ class PluginBuilder
+ def rakefile
+ template "Rakefile"
+ end
+
+ def app
+ if mountable?
+ if api?
+ directory 'app', exclude_pattern: %r{app/(views|helpers)}
+ else
+ directory 'app'
+ empty_directory_with_keep_file "app/assets/images/#{namespaced_name}"
+ end
+ elsif full?
+ empty_directory_with_keep_file 'app/models'
+ empty_directory_with_keep_file 'app/controllers'
+ empty_directory_with_keep_file 'app/mailers'
+
+ unless api?
+ empty_directory_with_keep_file "app/assets/images/#{namespaced_name}"
+ empty_directory_with_keep_file 'app/helpers'
+ empty_directory_with_keep_file 'app/views'
+ end
+ end
+ end
+
+ def readme
+ template "README.rdoc"
+ end
+
+ def gemfile
+ template "Gemfile"
+ end
+
+ def license
+ template "MIT-LICENSE"
+ end
+
+ def gemspec
+ template "%name%.gemspec"
+ end
+
+ def gitignore
+ template "gitignore", ".gitignore"
+ end
+
+ def lib
+ template "lib/%namespaced_name%.rb"
+ template "lib/tasks/%namespaced_name%_tasks.rake"
+ template "lib/%namespaced_name%/version.rb"
+ template "lib/%namespaced_name%/engine.rb" if engine?
+ end
+
+ def config
+ template "config/routes.rb" if engine?
+ end
+
+ def test
+ template "test/test_helper.rb"
+ template "test/%namespaced_name%_test.rb"
+ append_file "Rakefile", <<-EOF
+#{rakefile_test_tasks}
+
+task default: :test
+ EOF
+ if engine?
+ template "test/integration/navigation_test.rb"
+ end
+ end
+
+ PASSTHROUGH_OPTIONS = [
+ :skip_active_record, :skip_action_mailer, :skip_javascript, :database,
+ :javascript, :quiet, :pretend, :force, :skip
+ ]
+
+ def generate_test_dummy(force = false)
+ opts = (options || {}).slice(*PASSTHROUGH_OPTIONS)
+ opts[:force] = force
+ opts[:skip_bundle] = true
+ opts[:api] = options.api?
+
+ invoke Rails::Generators::AppGenerator,
+ [ File.expand_path(dummy_path, destination_root) ], opts
+ end
+
+ def test_dummy_config
+ template "rails/boot.rb", "#{dummy_path}/config/boot.rb", force: true
+ template "rails/application.rb", "#{dummy_path}/config/application.rb", force: true
+ if mountable?
+ template "rails/routes.rb", "#{dummy_path}/config/routes.rb", force: true
+ end
+ end
+
+ def test_dummy_assets
+ template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true
+ template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
+ template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/config/manifest.js", force: true
+ end
+
+ def test_dummy_clean
+ inside dummy_path do
+ remove_file ".gitignore"
+ remove_file "db/seeds.rb"
+ remove_file "doc"
+ remove_file "Gemfile"
+ remove_file "lib/tasks"
+ remove_file "public/robots.txt"
+ remove_file "README.md"
+ remove_file "test"
+ remove_file "vendor"
+ end
+ end
+
+ def assets_manifest
+ template "rails/engine_manifest.js", "app/assets/config/#{underscored_name}_manifest.js"
+ end
+
+ def stylesheets
+ if mountable?
+ copy_file "rails/stylesheets.css",
+ "app/assets/stylesheets/#{namespaced_name}/application.css"
+ elsif full?
+ empty_directory_with_keep_file "app/assets/stylesheets/#{namespaced_name}"
+ end
+ end
+
+ def javascripts
+ return if options.skip_javascript?
+
+ if mountable?
+ template "rails/javascripts.js",
+ "app/assets/javascripts/#{namespaced_name}/application.js"
+ elsif full?
+ empty_directory_with_keep_file "app/assets/javascripts/#{namespaced_name}"
+ end
+ end
+
+ def bin(force = false)
+ bin_file = engine? ? 'bin/rails.tt' : 'bin/test.tt'
+ template bin_file, force: force do |content|
+ "#{shebang}\n" + content
+ end
+ chmod "bin", 0755, verbose: false
+ end
+
+ def gemfile_entry
+ return unless inside_application?
+
+ gemfile_in_app_path = File.join(rails_app_path, "Gemfile")
+ if File.exist? gemfile_in_app_path
+ entry = "gem '#{name}', path: '#{relative_path}'"
+ append_file gemfile_in_app_path, entry
+ end
+ end
+ end
+
+ module Generators
+ class PluginGenerator < AppBase # :nodoc:
+ add_shared_options_for "plugin"
+
+ alias_method :plugin_path, :app_path
+
+ class_option :dummy_path, type: :string, default: "test/dummy",
+ desc: "Create dummy application at given path"
+
+ class_option :full, type: :boolean, default: false,
+ desc: "Generate a rails engine with bundled Rails application for testing"
+
+ class_option :mountable, type: :boolean, default: false,
+ desc: "Generate mountable isolated application"
+
+ class_option :skip_gemspec, type: :boolean, default: false,
+ desc: "Skip gemspec file"
+
+ class_option :skip_gemfile_entry, type: :boolean, default: false,
+ desc: "If creating plugin in application's directory " +
+ "skip adding entry to Gemfile"
+
+ class_option :api, type: :boolean, default: false,
+ desc: "Generate a smaller stack for API application plugins"
+
+ def initialize(*args)
+ @dummy_path = nil
+ super
+
+ unless plugin_path
+ raise Error, "Plugin name should be provided in arguments. For details run: rails plugin new --help"
+ end
+ end
+
+ public_task :set_default_accessors!
+ public_task :create_root
+
+ def create_root_files
+ build(:readme)
+ build(:rakefile)
+ build(:gemspec) unless options[:skip_gemspec]
+ build(:license)
+ build(:gitignore) unless options[:skip_git]
+ build(:gemfile) unless options[:skip_gemfile]
+ end
+
+ def create_app_files
+ build(:app)
+ end
+
+ def create_config_files
+ build(:config)
+ end
+
+ def create_lib_files
+ build(:lib)
+ end
+
+ def create_assets_manifest_file
+ build(:assets_manifest) if !api? && engine?
+ end
+
+ def create_public_stylesheets_files
+ build(:stylesheets) unless api?
+ end
+
+ def create_javascript_files
+ build(:javascripts) unless api?
+ end
+
+ def create_bin_files
+ build(:bin)
+ end
+
+ def create_test_files
+ build(:test) unless options[:skip_test]
+ end
+
+ def create_test_dummy_files
+ return unless with_dummy_app?
+ create_dummy_app
+ end
+
+ def update_gemfile
+ build(:gemfile_entry) unless options[:skip_gemfile_entry]
+ end
+
+ def finish_template
+ build(:leftovers)
+ end
+
+ public_task :apply_rails_template, :run_bundle
+
+ def name
+ @name ||= begin
+ # same as ActiveSupport::Inflector#underscore except not replacing '-'
+ underscored = original_name.dup
+ underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
+ underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
+ underscored.downcase!
+
+ underscored
+ end
+ end
+
+ def underscored_name
+ @underscored_name ||= original_name.underscore
+ end
+
+ def namespaced_name
+ @namespaced_name ||= name.gsub('-', '/')
+ end
+
+ protected
+
+ def app_templates_dir
+ "../../app/templates"
+ end
+
+ def create_dummy_app(path = nil)
+ dummy_path(path) if path
+
+ say_status :vendor_app, dummy_path
+ mute do
+ build(:generate_test_dummy)
+ store_application_definition!
+ build(:test_dummy_config)
+ build(:test_dummy_assets)
+ build(:test_dummy_clean)
+ # ensure that bin/rails has proper dummy_path
+ build(:bin, true)
+ end
+ end
+
+ def engine?
+ full? || mountable?
+ end
+
+ def full?
+ options[:full]
+ end
+
+ def mountable?
+ options[:mountable]
+ end
+
+ def skip_git?
+ options[:skip_git]
+ end
+
+ def with_dummy_app?
+ options[:skip_test].blank? || options[:dummy_path] != 'test/dummy'
+ end
+
+ def api?
+ options[:api]
+ end
+
+ def self.banner
+ "rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]"
+ end
+
+ def original_name
+ @original_name ||= File.basename(destination_root)
+ end
+
+ def modules
+ @modules ||= namespaced_name.camelize.split("::")
+ end
+
+ def wrap_in_modules(unwrapped_code)
+ unwrapped_code = "#{unwrapped_code}".strip.gsub(/\W$\n/, '')
+ modules.reverse.inject(unwrapped_code) do |content, mod|
+ str = "module #{mod}\n"
+ str += content.lines.map { |line| " #{line}" }.join
+ str += content.present? ? "\nend" : "end"
+ end
+ end
+
+ def camelized_modules
+ @camelized_modules ||= namespaced_name.camelize
+ end
+
+ def humanized
+ @humanized ||= original_name.underscore.humanize
+ end
+
+ def camelized
+ @camelized ||= name.gsub(/\W/, '_').squeeze('_').camelize
+ end
+
+ def author
+ default = "TODO: Write your name"
+ if skip_git?
+ @author = default
+ else
+ @author = `git config user.name`.chomp rescue default
+ end
+ end
+
+ def email
+ default = "TODO: Write your email address"
+ if skip_git?
+ @email = default
+ else
+ @email = `git config user.email`.chomp rescue default
+ end
+ end
+
+ def valid_const?
+ if original_name =~ /-\d/
+ raise Error, "Invalid plugin name #{original_name}. Please give a name which does not contain a namespace starting with numeric characters."
+ elsif original_name =~ /[^\w-]+/
+ raise Error, "Invalid plugin name #{original_name}. Please give a name which uses only alphabetic, numeric, \"_\" or \"-\" characters."
+ elsif camelized =~ /^\d/
+ raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers."
+ elsif RESERVED_NAMES.include?(name)
+ raise Error, "Invalid plugin name #{original_name}. Please give a " \
+ "name which does not match one of the reserved rails " \
+ "words: #{RESERVED_NAMES.join(", ")}"
+ elsif Object.const_defined?(camelized)
+ raise Error, "Invalid plugin name #{original_name}, constant #{camelized} is already in use. Please choose another plugin name."
+ end
+ end
+
+ def application_definition
+ @application_definition ||= begin
+
+ dummy_application_path = File.expand_path("#{dummy_path}/config/application.rb", destination_root)
+ unless options[:pretend] || !File.exist?(dummy_application_path)
+ contents = File.read(dummy_application_path)
+ contents[(contents.index(/module ([\w]+)\n(.*)class Application/m))..-1]
+ end
+ end
+ end
+ alias :store_application_definition! :application_definition
+
+ def get_builder_class
+ defined?(::PluginBuilder) ? ::PluginBuilder : Rails::PluginBuilder
+ end
+
+ def rakefile_test_tasks
+ <<-RUBY
+require 'rake/testtask'
+
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.libs << 'test'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = false
+end
+ RUBY
+ end
+
+ def dummy_path(path = nil)
+ @dummy_path = path if path
+ @dummy_path || options[:dummy_path]
+ end
+
+ def mute(&block)
+ shell.mute(&block)
+ end
+
+ def rails_app_path
+ APP_PATH.sub("/config/application", "") if defined?(APP_PATH)
+ end
+
+ def inside_application?
+ rails_app_path && app_path =~ /^#{rails_app_path}/
+ end
+
+ def relative_path
+ return unless inside_application?
+ app_path.sub(/^#{rails_app_path}\//, '')
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec
new file mode 100644
index 0000000000..3f5682b0c1
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec
@@ -0,0 +1,24 @@
+$:.push File.expand_path("../lib", __FILE__)
+
+# Maintain your gem's version:
+require "<%= namespaced_name %>/version"
+
+# Describe your gem and declare its dependencies:
+Gem::Specification.new do |s|
+ s.name = "<%= name %>"
+ s.version = <%= camelized_modules %>::VERSION
+ s.authors = ["<%= author %>"]
+ s.email = ["<%= email %>"]
+ s.homepage = "TODO"
+ s.summary = "TODO: Summary of <%= camelized_modules %>."
+ s.description = "TODO: Description of <%= camelized_modules %>."
+ s.license = "MIT"
+
+ s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
+
+ <%= '# ' if options.dev? || options.edge? -%>s.add_dependency "rails", "~> <%= Rails::VERSION::STRING %>"
+<% unless options[:skip_active_record] -%>
+
+ s.add_development_dependency "<%= gem_for_database[0] %>"
+<% end -%>
+end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/Gemfile b/railties/lib/rails/generators/rails/plugin/templates/Gemfile
new file mode 100644
index 0000000000..f085a2577a
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/Gemfile
@@ -0,0 +1,47 @@
+source 'https://rubygems.org'
+
+<% if options[:skip_gemspec] -%>
+<%= '# ' if options.dev? || options.edge? -%>gem 'rails', '~> <%= Rails::VERSION::STRING %>'
+<% else -%>
+# Declare your gem's dependencies in <%= name %>.gemspec.
+# Bundler will treat runtime dependencies like base dependencies, and
+# development dependencies will be added by default to the :development group.
+gemspec
+<% end -%>
+
+<% if options[:skip_gemspec] -%>
+group :development do
+ gem '<%= gem_for_database[0] %>'
+end
+<% else -%>
+# Declare any dependencies that are still in development here instead of in
+# your gemspec. These might include edge Rails or gems from your path or
+# Git. Remember to move these dependencies to your gemspec before releasing
+# your gem to rubygems.org.
+<% end -%>
+
+<% if options.dev? || options.edge? -%>
+# Your gem is dependent on dev or edge Rails. Once you can lock this
+# dependency down to a specific version, move it to your gemspec.
+<% max_width = gemfile_entries.map { |g| g.name.length }.max -%>
+<% gemfile_entries.each do |gem| -%>
+<% if gem.comment -%>
+
+# <%= gem.comment %>
+<% end -%>
+<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%>
+<% if gem.options.any? -%>
+, <%= gem.options.map { |k,v|
+ "#{k}: #{v.inspect}" }.join(', ') %>
+<% end -%>
+<% end -%>
+
+<% end -%>
+<% if RUBY_ENGINE == 'ruby' -%>
+# To use a debugger
+# gem 'byebug', group: [:development, :test]
+<% end -%>
+<% if RUBY_PLATFORM.match(/bccwin|cygwin|emx|mingw|mswin|wince|java/) -%>
+
+gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE
new file mode 100644
index 0000000000..ff2fb3ba4e
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright <%= Date.today.year %> <%= author %>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/railties/lib/rails/generators/rails/plugin/templates/README.rdoc b/railties/lib/rails/generators/rails/plugin/templates/README.rdoc
new file mode 100644
index 0000000000..25983ca5da
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/README.rdoc
@@ -0,0 +1,3 @@
+= <%= camelized_modules %>
+
+This project rocks and uses MIT-LICENSE. \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile
new file mode 100644
index 0000000000..bda55bae29
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile
@@ -0,0 +1,29 @@
+begin
+ require 'bundler/setup'
+rescue LoadError
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
+end
+
+require 'rdoc/task'
+
+RDoc::Task.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = '<%= camelized_modules %>'
+ rdoc.options << '--line-numbers'
+ rdoc.rdoc_files.include('README.rdoc')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+<% if engine? && !options[:skip_active_record] && with_dummy_app? -%>
+APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__)
+load 'rails/tasks/engine.rake'
+<% end %>
+
+<% if engine? -%>
+load 'rails/tasks/statistics.rake'
+<% end %>
+
+<% unless options[:skip_gemspec] -%>
+
+Bundler::GemHelper.install_tasks
+<% end %>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt
new file mode 100644
index 0000000000..7fe4e5034d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt
@@ -0,0 +1,5 @@
+<%= wrap_in_modules <<-rb.strip_heredoc
+ class ApplicationController < ActionController::#{api? ? "API" : "Base"}
+ end
+rb
+%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt
new file mode 100644
index 0000000000..25d692732d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt
@@ -0,0 +1,5 @@
+<%= wrap_in_modules <<-rb.strip_heredoc
+ module ApplicationHelper
+ end
+rb
+%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt
new file mode 100644
index 0000000000..bad1ff2d16
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt
@@ -0,0 +1,5 @@
+<%= wrap_in_modules <<-rb.strip_heredoc
+ class ApplicationJob < ActiveJob::Base
+ end
+rb
+%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory
diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory b/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory
diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt
new file mode 100644
index 0000000000..6bc480161d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title><%= humanized %></title>
+ <%%= stylesheet_link_tag "<%= namespaced_name %>/application", media: "all" %>
+ <%%= javascript_include_tag "<%= namespaced_name %>/application" %>
+ <%%= csrf_meta_tags %>
+</head>
+<body>
+
+<%%= yield %>
+
+</body>
+</html>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt
new file mode 100644
index 0000000000..3edaac35c9
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt
@@ -0,0 +1,11 @@
+# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
+
+ENGINE_ROOT = File.expand_path('../..', __FILE__)
+ENGINE_PATH = File.expand_path('../../lib/<%= namespaced_name -%>/engine', __FILE__)
+
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+
+require 'rails/all'
+require 'rails/engine/commands'
diff --git a/railties/lib/rails/generators/rails/plugin/templates/bin/test.tt b/railties/lib/rails/generators/rails/plugin/templates/bin/test.tt
new file mode 100644
index 0000000000..62b94618fd
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/bin/test.tt
@@ -0,0 +1,8 @@
+$: << File.expand_path(File.expand_path('../../test', __FILE__))
+
+require 'bundler/setup'
+require 'rails/test_unit/minitest_plugin'
+
+Rails::TestUnitReporter.executable = 'bin/test'
+
+exit Minitest.run(ARGV)
diff --git a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb
new file mode 100644
index 0000000000..154452bfe5
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb
@@ -0,0 +1,6 @@
+<% if mountable? -%>
+<%= camelized_modules %>::Engine.routes.draw do
+<% else -%>
+Rails.application.routes.draw do
+<% end -%>
+end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore
new file mode 100644
index 0000000000..54c78d7927
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore
@@ -0,0 +1,9 @@
+.bundle/
+log/*.log
+pkg/
+<% unless options[:skip_test] && options[:dummy_path] == 'test/dummy' -%>
+<%= dummy_path %>/db/*.sqlite3
+<%= dummy_path %>/db/*.sqlite3-journal
+<%= dummy_path %>/log/*.log
+<%= dummy_path %>/tmp/
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb
new file mode 100644
index 0000000000..40b1c4cee7
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb
@@ -0,0 +1,5 @@
+<% if engine? -%>
+require "<%= namespaced_name %>/engine"
+
+<% end -%>
+<%= wrap_in_modules "# Your code goes here..." %>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb
new file mode 100644
index 0000000000..8938770fc4
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb
@@ -0,0 +1,7 @@
+<%= wrap_in_modules <<-rb.strip_heredoc
+ class Engine < ::Rails::Engine
+ #{mountable? ? ' isolate_namespace ' + camelized_modules : ' '}
+ #{api? ? " config.generators.api_only = true" : ' '}
+ end
+rb
+%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb
new file mode 100644
index 0000000000..b08f4ef9ae
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb
@@ -0,0 +1 @@
+<%= wrap_in_modules "VERSION = '0.1.0'" %>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake
new file mode 100644
index 0000000000..88a2c4120f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :<%= underscored_name %> do
+# # Task goes here
+# end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb
new file mode 100644
index 0000000000..b1038c839e
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../boot', __FILE__)
+
+<% if include_all_railties? -%>
+require 'rails/all'
+<% else -%>
+# Pick the frameworks you want:
+<%= comment_if :skip_active_record %>require "active_record/railtie"
+require "action_controller/railtie"
+<%= comment_if :skip_action_mailer %>require "action_mailer/railtie"
+require "action_view/railtie"
+<%= comment_if :skip_sprockets %>require "sprockets/railtie"
+<%= comment_if :skip_test %>require "rails/test_unit/railtie"
+<% end -%>
+
+Bundler.require(*Rails.groups)
+require "<%= namespaced_name %>"
+
+<%= application_definition %>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb
new file mode 100644
index 0000000000..6266cfc509
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb
@@ -0,0 +1,5 @@
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
+
+require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
new file mode 100644
index 0000000000..8d21b2b6fb
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
@@ -0,0 +1,11 @@
+
+<% unless api? -%>
+//= link_tree ../images
+<% end -%>
+<% unless options.skip_javascript -%>
+//= link_directory ../javascripts .js
+<% end -%>
+//= link_directory ../stylesheets .css
+<% if mountable? && !api? -%>
+//= link <%= underscored_name %>_manifest.js
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js
new file mode 100644
index 0000000000..2f23844f5e
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js
@@ -0,0 +1,6 @@
+<% if mountable? -%>
+<% if !options.skip_javascript -%>
+//= link_directory ../javascripts/<%= namespaced_name %> .js
+<% end -%>
+//= link_directory ../stylesheets/<%= namespaced_name %> .css
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js
new file mode 100644
index 0000000000..e54c6461cc
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js
@@ -0,0 +1,13 @@
+// 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, vendor/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
+// compiled file. JavaScript code in this file should be added after the last require_* statement.
+//
+// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
+// about supported directives.
+//
+//= require_tree .
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb
new file mode 100644
index 0000000000..694510edc0
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb
@@ -0,0 +1,3 @@
+Rails.application.routes.draw do
+ mount <%= camelized_modules %>::Engine => "/<%= name %>"
+end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css b/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css
new file mode 100644
index 0000000000..0ebd7fe829
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css
@@ -0,0 +1,15 @@
+/*
+ * 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, vendor/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
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
+ * files in this directory. Styles in this file should be added after the last require_* statement.
+ * It is generally better to create a new file per style scope.
+ *
+ *= require_tree .
+ *= require_self
+ */
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb
new file mode 100644
index 0000000000..1ee05d7871
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class <%= camelized_modules %>::Test < ActiveSupport::TestCase
+ test "truth" do
+ assert_kind_of Module, <%= camelized_modules %>
+ end
+end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
new file mode 100644
index 0000000000..f5d1ec2046
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class NavigationTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
+
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
new file mode 100644
index 0000000000..a0b00fc5c5
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
@@ -0,0 +1,27 @@
+# Configure Rails Environment
+ENV["RAILS_ENV"] = "test"
+
+require File.expand_path("../../<%= options[:dummy_path] -%>/config/environment.rb", __FILE__)
+<% unless options[:skip_active_record] -%>
+ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../<%= options[:dummy_path] -%>/db/migrate", __FILE__)]
+<% if options[:mountable] -%>
+ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__)
+<% end -%>
+<% end -%>
+require "rails/test_help"
+
+# Filter out Minitest backtrace while allowing backtrace from other libraries
+# to be shown.
+Minitest.backtrace_filter = Minitest::BacktraceFilter.new
+
+<% unless engine? -%>
+Rails::TestUnitReporter.executable = 'bin/test'
+<% end -%>
+
+# Load fixtures from the engine
+if ActiveSupport::TestCase.respond_to?(:fixture_path=)
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
+ ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
+ ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
+ ActiveSupport::TestCase.fixtures :all
+end
diff --git a/railties/lib/rails/generators/rails/resource/USAGE b/railties/lib/rails/generators/rails/resource/USAGE
new file mode 100644
index 0000000000..e359cd574f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/resource/USAGE
@@ -0,0 +1,23 @@
+Description:
+ Stubs out a new resource including an empty model and controller suitable
+ for a restful, resource-oriented application. Pass the singular model name,
+ either CamelCased or under_scored, as the first argument, and an optional
+ list of attribute pairs.
+
+ Attribute pairs are field:type arguments specifying the
+ model's attributes. Timestamps are added by default, so you don't have to
+ specify them by hand as 'created_at:datetime updated_at:datetime'.
+
+ You don't have to think up every attribute up front, but it helps to
+ sketch out a few so you can start working with the model immediately.
+
+ This generator invokes your configured ORM and test framework, besides
+ creating helpers and add routes to config/routes.rb.
+
+ Unlike the scaffold generator, the resource generator does not create
+ views or add any methods to the generated controller.
+
+Examples:
+ `rails generate resource post` # no attributes
+ `rails generate resource post title:string body:text published:boolean`
+ `rails generate resource purchase order_id:integer amount:decimal`
diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb
new file mode 100644
index 0000000000..3acf21df13
--- /dev/null
+++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb
@@ -0,0 +1,19 @@
+require 'rails/generators/resource_helpers'
+require 'rails/generators/rails/model/model_generator'
+
+module Rails
+ module Generators
+ class ResourceGenerator < ModelGenerator # :nodoc:
+ include ResourceHelpers
+
+ hook_for :resource_controller, required: true do |controller|
+ invoke controller, [ controller_name, options[:actions] ]
+ end
+
+ class_option :actions, type: :array, banner: "ACTION ACTION", default: [],
+ desc: "Actions for the resource controller"
+
+ hook_for :resource_route, required: true
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
new file mode 100644
index 0000000000..42705107ae
--- /dev/null
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -0,0 +1,51 @@
+module Rails
+ module Generators
+ class ResourceRouteGenerator < NamedBase # :nodoc:
+ # Properly nests namespaces passed into a generator
+ #
+ # $ rails generate resource admin/users/products
+ #
+ # should give you
+ #
+ # namespace :admin do
+ # namespace :users do
+ # resources :products
+ # end
+ # end
+ def add_resource_route
+ return if options[:actions].present?
+
+ # iterates over all namespaces and opens up blocks
+ regular_class_path.each_with_index do |namespace, index|
+ write("namespace :#{namespace} do", index + 1)
+ end
+
+ # inserts the primary resource
+ write("resources :#{file_name.pluralize}", route_length + 1)
+
+ # ends blocks
+ regular_class_path.each_index do |index|
+ write("end", route_length - index)
+ end
+
+ # route prepends two spaces onto the front of the string that is passed, this corrects that.
+ # Also it adds a \n to the end of each line, as route already adds that
+ # we need to correct that too.
+ route route_string[2..-2]
+ end
+
+ private
+ def route_string
+ @route_string ||= ""
+ end
+
+ def write(str, indent)
+ route_string << "#{" " * indent}#{str}\n"
+ end
+
+ def route_length
+ regular_class_path.length
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE
new file mode 100644
index 0000000000..d2e495758d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold/USAGE
@@ -0,0 +1,41 @@
+Description:
+ Scaffolds an entire resource, from model and migration to controller and
+ views, along with a full test suite. The resource is ready to use as a
+ starting point for your RESTful, resource-oriented application.
+
+ Pass the name of the model (in singular form), either CamelCased or
+ under_scored, as the first argument, and an optional list of attribute
+ pairs.
+
+ Attributes are field arguments specifying the model's attributes. You can
+ optionally pass the type and an index to each field. For instance:
+ 'title body:text tracking_id:integer:uniq' will generate a title field of
+ string type, a body with text type and a tracking_id as an integer with an
+ unique index. "index" could also be given instead of "uniq" if one desires
+ a non unique index.
+
+ As a special case, specifying 'password:digest' will generate a
+ password_digest field of string type, and configure your generated model,
+ controller, views, and test suite for use with ActiveModel
+ has_secure_password (assuming they are using Rails defaults).
+
+ Timestamps are added by default, so you don't have to specify them by hand
+ as 'created_at:datetime updated_at:datetime'.
+
+ You don't have to think up every attribute up front, but it helps to
+ sketch out a few so you can start working with the resource immediately.
+
+ For example, 'scaffold post title body:text published:boolean' gives
+ you a model with those three attributes, a controller that handles
+ the create/show/update/destroy, forms to create and edit your posts, and
+ an index that lists them all, as well as a resources :posts declaration
+ in config/routes.rb.
+
+ If you want to remove all the generated files, run
+ 'rails destroy scaffold ModelName'.
+
+Examples:
+ `rails generate scaffold post`
+ `rails generate scaffold post title:string body:text published:boolean`
+ `rails generate scaffold purchase amount:decimal tracking_id:integer:uniq`
+ `rails generate scaffold user email:uniq password:digest`
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..17c32bfdb3
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -0,0 +1,33 @@
+require 'rails/generators/rails/resource/resource_generator'
+
+module Rails
+ module Generators
+ class ScaffoldGenerator < ResourceGenerator # :nodoc:
+ remove_hook_for :resource_controller
+ remove_class_option :actions
+
+ class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
+ class_option :stylesheet_engine, desc: "Engine for Stylesheets"
+ class_option :assets, type: :boolean
+ class_option :resource_route, type: :boolean
+ class_option :scaffold_stylesheet, type: :boolean
+
+ def handle_skip
+ @options = @options.merge(stylesheets: false) unless options[:assets]
+ @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets] && options[:scaffold_stylesheet]
+ end
+
+ hook_for :scaffold_controller, required: true
+
+ hook_for :assets do |assets|
+ invoke assets, [controller_name]
+ end
+
+ hook_for :stylesheet_engine do |stylesheet_engine|
+ if behavior == :invoke
+ invoke stylesheet_engine, [controller_name]
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
new file mode 100644
index 0000000000..79f8b7f96f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
@@ -0,0 +1,84 @@
+body {
+ background-color: #fff;
+ color: #333;
+}
+
+body, p, ol, ul, td {
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+ margin: 33px;
+}
+
+pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px;
+}
+
+a {
+ color: #000;
+}
+
+a:visited {
+ color: #666;
+}
+
+a:hover {
+ color: #fff;
+ background-color: #000;
+}
+
+th {
+ padding-bottom: 5px;
+}
+
+td {
+ padding-bottom: 7px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+div.field,
+div.actions {
+ margin-bottom: 10px;
+}
+
+#notice {
+ color: green;
+}
+
+.field_with_errors {
+ padding: 2px;
+ background-color: red;
+ display: table;
+}
+
+#error_explanation {
+ width: 450px;
+ border: 2px solid red;
+ padding: 7px;
+ padding-bottom: 0;
+ margin-bottom: 20px;
+ background-color: #f0f0f0;
+}
+
+#error_explanation h2 {
+ text-align: left;
+ font-weight: bold;
+ padding: 5px 5px 5px 15px;
+ font-size: 12px;
+ margin: -7px;
+ margin-bottom: 0;
+ background-color: #c00;
+ color: #fff;
+}
+
+#error_explanation ul li {
+ font-size: 12px;
+ list-style: square;
+}
+
+label {
+ display: block;
+}
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/USAGE b/railties/lib/rails/generators/rails/scaffold_controller/USAGE
new file mode 100644
index 0000000000..8ba4c5ccbc
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold_controller/USAGE
@@ -0,0 +1,19 @@
+Description:
+ Stubs out a scaffolded controller, its seven RESTful actions and related
+ views. Pass the model name, either CamelCased or under_scored. The
+ controller name is retrieved as a pluralized version of the model name.
+
+ To create a controller within a module, specify the model name as a
+ path like 'parent_module/controller_name'.
+
+ This generates a controller class in app/controllers and invokes helper,
+ template engine and test framework generators.
+
+Example:
+ `rails generate scaffold_controller CreditCard`
+
+ Credit card controller with URLs like /credit_card/debit.
+ Controller: app/controllers/credit_cards_controller.rb
+ Test: test/controllers/credit_cards_controller_test.rb
+ Views: app/views/credit_cards/index.html.erb [...]
+ Helper: app/helpers/credit_cards_helper.rb
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
new file mode 100644
index 0000000000..d0b8cad896
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
@@ -0,0 +1,31 @@
+require 'rails/generators/resource_helpers'
+
+module Rails
+ module Generators
+ class ScaffoldControllerGenerator < NamedBase # :nodoc:
+ include ResourceHelpers
+
+ check_class_collision suffix: "Controller"
+
+ class_option :helper, type: :boolean
+ class_option :orm, banner: "NAME", type: :string, required: true,
+ desc: "ORM to generate the controller for"
+ class_option :api, type: :boolean,
+ desc: "Generates API controller"
+
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
+
+ def create_controller_files
+ template_file = options.api? ? "api_controller.rb" : "controller.rb"
+ template template_file, File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
+ end
+
+ hook_for :template_engine, :test_framework, as: :scaffold
+
+ # Invoke the helper using the controller name (pluralized)
+ hook_for :helper, as: :scaffold do |invoked|
+ invoke invoked, [ controller_name ]
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
new file mode 100644
index 0000000000..400afec6dc
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
@@ -0,0 +1,61 @@
+<% if namespaced? -%>
+require_dependency "<%= namespaced_path %>/application_controller"
+
+<% end -%>
+<% module_namespacing do -%>
+class <%= controller_class_name %>Controller < ApplicationController
+ before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy]
+
+ # GET <%= route_url %>
+ def index
+ @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
+
+ render json: <%= "@#{plural_table_name}" %>
+ end
+
+ # GET <%= route_url %>/1
+ def show
+ render json: <%= "@#{singular_table_name}" %>
+ end
+
+ # POST <%= route_url %>
+ def create
+ @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
+
+ if @<%= orm_instance.save %>
+ render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %>
+ else
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
+ end
+ end
+
+ # PATCH/PUT <%= route_url %>/1
+ def update
+ if @<%= orm_instance.update("#{singular_table_name}_params") %>
+ render json: <%= "@#{singular_table_name}" %>
+ else
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
+ end
+ end
+
+ # DELETE <%= route_url %>/1
+ def destroy
+ @<%= orm_instance.destroy %>
+ end
+
+ private
+ # Use callbacks to share common setup or constraints between actions.
+ def set_<%= singular_table_name %>
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
+ end
+
+ # Only allow a trusted parameter "white list" through.
+ def <%= "#{singular_table_name}_params" %>
+ <%- if attributes_names.empty? -%>
+ params.fetch(:<%= singular_table_name %>, {})
+ <%- else -%>
+ params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
+ <%- end -%>
+ end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
new file mode 100644
index 0000000000..42b9e34274
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -0,0 +1,68 @@
+<% if namespaced? -%>
+require_dependency "<%= namespaced_path %>/application_controller"
+
+<% end -%>
+<% module_namespacing do -%>
+class <%= controller_class_name %>Controller < ApplicationController
+ before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
+
+ # GET <%= route_url %>
+ def index
+ @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
+ end
+
+ # GET <%= route_url %>/1
+ def show
+ end
+
+ # GET <%= route_url %>/new
+ def new
+ @<%= singular_table_name %> = <%= orm_class.build(class_name) %>
+ end
+
+ # GET <%= route_url %>/1/edit
+ def edit
+ end
+
+ # POST <%= route_url %>
+ def create
+ @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
+
+ if @<%= orm_instance.save %>
+ redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %>
+ else
+ render :new
+ end
+ end
+
+ # PATCH/PUT <%= route_url %>/1
+ def update
+ if @<%= orm_instance.update("#{singular_table_name}_params") %>
+ redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %>
+ else
+ render :edit
+ end
+ end
+
+ # DELETE <%= route_url %>/1
+ def destroy
+ @<%= orm_instance.destroy %>
+ redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %>
+ end
+
+ private
+ # Use callbacks to share common setup or constraints between actions.
+ def set_<%= singular_table_name %>
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
+ end
+
+ # Only allow a trusted parameter "white list" through.
+ def <%= "#{singular_table_name}_params" %>
+ <%- if attributes_names.empty? -%>
+ params.fetch(:<%= singular_table_name %>, {})
+ <%- else -%>
+ params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
+ <%- end -%>
+ end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/task/USAGE b/railties/lib/rails/generators/rails/task/USAGE
new file mode 100644
index 0000000000..dbe9bbaf08
--- /dev/null
+++ b/railties/lib/rails/generators/rails/task/USAGE
@@ -0,0 +1,9 @@
+Description:
+ Stubs out a new Rake task. Pass the namespace name, and a list of tasks as arguments.
+
+ This generates a task file in lib/tasks.
+
+Example:
+ `rails generate task feeds fetch erase add`
+
+ Task: lib/tasks/feeds.rake \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/task/task_generator.rb b/railties/lib/rails/generators/rails/task/task_generator.rb
new file mode 100644
index 0000000000..754824ca0c
--- /dev/null
+++ b/railties/lib/rails/generators/rails/task/task_generator.rb
@@ -0,0 +1,12 @@
+module Rails
+ module Generators
+ class TaskGenerator < NamedBase # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
+
+ def create_task_files
+ template 'task.rb', File.join('lib/tasks', "#{file_name}.rake")
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb
new file mode 100644
index 0000000000..1e3ed5f158
--- /dev/null
+++ b/railties/lib/rails/generators/rails/task/templates/task.rb
@@ -0,0 +1,8 @@
+namespace :<%= file_name %> do
+<% actions.each do |action| -%>
+ desc "TODO"
+ task <%= action %>: :environment do
+ end
+
+<% end -%>
+end
diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb
new file mode 100644
index 0000000000..9c2037783e
--- /dev/null
+++ b/railties/lib/rails/generators/resource_helpers.rb
@@ -0,0 +1,82 @@
+require 'rails/generators/active_model'
+require 'rails/generators/model_helpers'
+
+module Rails
+ module Generators
+ # Deal with controller names on scaffold and add some helpers to deal with
+ # ActiveModel.
+ module ResourceHelpers # :nodoc:
+
+ def self.included(base) #:nodoc:
+ base.include(Rails::Generators::ModelHelpers)
+ base.class_option :model_name, type: :string, desc: "ModelName to be used"
+ end
+
+ # Set controller variables on initialization.
+ def initialize(*args) #:nodoc:
+ super
+ controller_name = name
+ if options[:model_name]
+ self.name = options[:model_name]
+ assign_names!(self.name)
+ end
+
+ assign_controller_names!(controller_name.pluralize)
+ end
+
+ protected
+
+ attr_reader :controller_name, :controller_file_name
+
+ def controller_class_path
+ if options[:model_name]
+ @controller_class_path
+ else
+ class_path
+ end
+ end
+
+ def assign_controller_names!(name)
+ @controller_name = name
+ @controller_class_path = name.include?('/') ? name.split('/') : name.split('::')
+ @controller_class_path.map!(&:underscore)
+ @controller_file_name = @controller_class_path.pop
+ end
+
+ def controller_file_path
+ @controller_file_path ||= (controller_class_path + [controller_file_name]).join('/')
+ end
+
+ def controller_class_name
+ (controller_class_path + [controller_file_name]).map!(&:camelize).join('::')
+ end
+
+ def controller_i18n_scope
+ @controller_i18n_scope ||= controller_file_path.tr('/', '.')
+ end
+
+ # Loads the ORM::Generators::ActiveModel class. This class is responsible
+ # to tell scaffold entities how to generate an specific method for the
+ # ORM. Check Rails::Generators::ActiveModel for more information.
+ def orm_class
+ @orm_class ||= begin
+ # Raise an error if the class_option :orm was not defined.
+ unless self.class.class_options[:orm]
+ raise "You need to have :orm as class option to invoke orm_class and orm_instance"
+ end
+
+ begin
+ "#{options[:orm].to_s.camelize}::Generators::ActiveModel".constantize
+ rescue NameError
+ Rails::Generators::ActiveModel
+ end
+ end
+ end
+
+ # Initialize ORM::Generators::ActiveModel to access instance methods.
+ def orm_instance(name=singular_table_name)
+ @orm_instance ||= orm_class.new(name)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
new file mode 100644
index 0000000000..58592b4f8e
--- /dev/null
+++ b/railties/lib/rails/generators/test_case.rb
@@ -0,0 +1,36 @@
+require 'rails/generators'
+require 'rails/generators/testing/behaviour'
+require 'rails/generators/testing/setup_and_teardown'
+require 'rails/generators/testing/assertions'
+require 'fileutils'
+
+module Rails
+ module Generators
+ # Disable color in output. Easier to debug.
+ no_color!
+
+ # This class provides a TestCase for testing generators. To setup, you need
+ # just to configure the destination and set which generator is being tested:
+ #
+ # class AppGeneratorTest < Rails::Generators::TestCase
+ # tests AppGenerator
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ # end
+ #
+ # If you want to ensure your destination root is clean before running each test,
+ # you can set a setup callback:
+ #
+ # class AppGeneratorTest < Rails::Generators::TestCase
+ # tests AppGenerator
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ # setup :prepare_destination
+ # end
+ class TestCase < ActiveSupport::TestCase
+ include Rails::Generators::Testing::Behaviour
+ include Rails::Generators::Testing::SetupAndTeardown
+ include Rails::Generators::Testing::Assertions
+ include FileUtils
+
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit.rb b/railties/lib/rails/generators/test_unit.rb
new file mode 100644
index 0000000000..fe45c9e15d
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit.rb
@@ -0,0 +1,8 @@
+require 'rails/generators/named_base'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class Base < Rails::Generators::NamedBase # :nodoc:
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
new file mode 100644
index 0000000000..b5aa581769
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
@@ -0,0 +1,15 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class ControllerGenerator < Base # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
+ check_class_collision suffix: "ControllerTest"
+
+ def create_test_files
+ template 'functional_test.rb',
+ File.join('test/controllers', class_path, "#{file_name}_controller_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
new file mode 100644
index 0000000000..5a8a3ca5e0
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
@@ -0,0 +1,25 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= class_name %>ControllerTest < ActionController::TestCase
+<% if mountable_engine? -%>
+ setup do
+ @routes = Engine.routes
+ end
+
+<% end -%>
+<% if actions.empty? -%>
+ # test "the truth" do
+ # assert true
+ # end
+<% else -%>
+<% actions.each do |action| -%>
+ test "should get <%= action %>" do
+ get :<%= action %>
+ assert_response :success
+ end
+
+<% end -%>
+<% end -%>
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
new file mode 100644
index 0000000000..d7307398ce
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
@@ -0,0 +1,26 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class GeneratorGenerator < Base # :nodoc:
+ check_class_collision suffix: "GeneratorTest"
+
+ class_option :namespace, type: :boolean, default: true,
+ desc: "Namespace generator under lib/generators/name"
+
+ def create_generator_files
+ template 'generator_test.rb', File.join('test/lib/generators', class_path, "#{file_name}_generator_test.rb")
+ end
+
+ protected
+
+ def generator_path
+ if options[:namespace]
+ File.join("generators", regular_class_path, file_name, "#{file_name}_generator")
+ else
+ File.join("generators", regular_class_path, "#{file_name}_generator")
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb
new file mode 100644
index 0000000000..a7f1fc4fba
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb
@@ -0,0 +1,16 @@
+require 'test_helper'
+require '<%= generator_path %>'
+
+<% module_namespacing do -%>
+class <%= class_name %>GeneratorTest < Rails::Generators::TestCase
+ tests <%= class_name %>Generator
+ destination Rails.root.join('tmp/generators')
+ setup :prepare_destination
+
+ # test "generator runs without errors" do
+ # assert_nothing_raised do
+ # run_generator ["arguments"]
+ # end
+ # end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
new file mode 100644
index 0000000000..bde4e88915
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
@@ -0,0 +1,9 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class HelperGenerator < Base # :nodoc:
+ # Rails does not generate anything here.
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
new file mode 100644
index 0000000000..e004835bd5
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
@@ -0,0 +1,13 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class IntegrationGenerator < Base # :nodoc:
+ check_class_collision suffix: "Test"
+
+ def create_test_files
+ template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb
new file mode 100644
index 0000000000..dea7e22196
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class <%= class_name %>Test < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb
new file mode 100644
index 0000000000..566b61ca66
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb
@@ -0,0 +1,13 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class JobGenerator < Base # :nodoc:
+ check_class_collision suffix: 'JobTest'
+
+ def create_test_file
+ template 'unit_test.rb.erb', File.join('test/jobs', class_path, "#{file_name}_job_test.rb")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
new file mode 100644
index 0000000000..f5351d0ec6
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
@@ -0,0 +1,9 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= class_name %>JobTest < ActiveJob::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
new file mode 100644
index 0000000000..343c8a3949
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
@@ -0,0 +1,26 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class MailerGenerator < Base # :nodoc:
+ argument :actions, type: :array, default: [], banner: "method method"
+
+ def check_class_collision
+ class_collisions "#{class_name}MailerTest", "#{class_name}MailerPreview"
+ end
+
+ def create_test_files
+ template "functional_test.rb", File.join('test/mailers', class_path, "#{file_name}_mailer_test.rb")
+ end
+
+ def create_preview_files
+ template "preview.rb", File.join('test/mailers/previews', class_path, "#{file_name}_mailer_preview.rb")
+ end
+
+ protected
+ def file_name
+ @_file_name ||= super.gsub(/\_mailer/i, '')
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb
new file mode 100644
index 0000000000..a2f2d30de5
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb
@@ -0,0 +1,21 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= class_name %>MailerTest < ActionMailer::TestCase
+<% actions.each do |action| -%>
+ test "<%= action %>" do
+ mail = <%= class_name %>Mailer.<%= action %>
+ assert_equal <%= action.to_s.humanize.inspect %>, mail.subject
+ assert_equal ["to@example.org"], mail.to
+ assert_equal ["from@example.com"], mail.from
+ assert_match "Hi", mail.body.encoded
+ end
+
+<% end -%>
+<% if actions.blank? -%>
+ # test "the truth" do
+ # assert true
+ # end
+<% end -%>
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb
new file mode 100644
index 0000000000..b063cbc47b
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb
@@ -0,0 +1,13 @@
+<% module_namespacing do -%>
+# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>_mailer
+class <%= class_name %>MailerPreview < ActionMailer::Preview
+<% actions.each do |action| -%>
+
+ # Preview this email at http://localhost:3000/rails/mailers/<%= file_path %>_mailer/<%= action %>
+ def <%= action %>
+ <%= class_name %>Mailer.<%= action %>
+ end
+<% end -%>
+
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb
new file mode 100644
index 0000000000..086588750e
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb
@@ -0,0 +1,36 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class ModelGenerator < Base # :nodoc:
+
+ RESERVED_YAML_KEYWORDS = %w(y yes n no true false on off null)
+
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
+ class_option :fixture, type: :boolean
+
+ check_class_collision suffix: "Test"
+
+ def create_test_file
+ template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_test.rb")
+ end
+
+ hook_for :fixture_replacement
+
+ def create_fixture_file
+ if options[:fixture] && options[:fixture_replacement].nil?
+ template 'fixtures.yml', File.join('test/fixtures', class_path, "#{fixture_file_name}.yml")
+ end
+ end
+
+ private
+ def yaml_key_value(key, value)
+ if RESERVED_YAML_KEYWORDS.include?(key.downcase)
+ "'#{key}': #{value}"
+ else
+ "#{key}: #{value}"
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
new file mode 100644
index 0000000000..50ca61a35b
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
@@ -0,0 +1,29 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+<% unless attributes.empty? -%>
+<% %w(one two).each do |name| %>
+<%= name %>:
+<% attributes.each do |attribute| -%>
+ <%- if attribute.password_digest? -%>
+ password_digest: <%%= BCrypt::Password.create('secret') %>
+ <%- elsif attribute.reference? -%>
+ <%= yaml_key_value(attribute.column_name.sub(/_id$/, ''), attribute.default) %>
+ <%- else -%>
+ <%= yaml_key_value(attribute.column_name, attribute.default) %>
+ <%- end -%>
+ <%- if attribute.polymorphic? -%>
+ <%= yaml_key_value("#{attribute.name}_type", attribute.human_name) %>
+ <%- end -%>
+<% end -%>
+<% end -%>
+<% else -%>
+
+# This model initially had no columns defined. If you add columns to the
+# model remove the '{}' from the fixture names and add the columns immediately
+# below each fixture, per the syntax in the comments below
+#
+one: {}
+# column: value
+#
+two: {}
+# column: value
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb
new file mode 100644
index 0000000000..c9bc7d5b90
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb
@@ -0,0 +1,9 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= class_name %>Test < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
new file mode 100644
index 0000000000..b5d4f38444
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
@@ -0,0 +1,13 @@
+require 'rails/generators/test_unit'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class PluginGenerator < Base # :nodoc:
+ check_class_collision suffix: "Test"
+
+ def create_test_files
+ directory '.', 'test'
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt b/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt
new file mode 100644
index 0000000000..0cbae1120e
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class <%= class_name %>Test < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
new file mode 100644
index 0000000000..30a861f09d
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
@@ -0,0 +1,2 @@
+require 'active_support/testing/autorun'
+require 'active_support'
diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..0171da7cc7
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -0,0 +1,46 @@
+require 'rails/generators/test_unit'
+require 'rails/generators/resource_helpers'
+
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class ScaffoldGenerator < Base # :nodoc:
+ include Rails::Generators::ResourceHelpers
+
+ check_class_collision suffix: "ControllerTest"
+
+ class_option :api, type: :boolean,
+ desc: "Generates API functional tests"
+
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
+
+ def create_test_files
+ template_file = options.api? ? "api_functional_test.rb" : "functional_test.rb"
+ template template_file,
+ File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb")
+ end
+
+ def fixture_name
+ @fixture_name ||=
+ if mountable_engine?
+ "%s_%s" % [namespaced_path, table_name]
+ else
+ table_name
+ end
+ end
+
+ private
+
+ def attributes_hash
+ return if attributes_names.empty?
+
+ attributes_names.map do |name|
+ if %w(password password_confirmation).include?(name) && attributes.any?(&:password_digest?)
+ "#{name}: 'secret'"
+ else
+ "#{name}: @#{singular_table_name}.#{name}"
+ end
+ end.sort.join(', ')
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
new file mode 100644
index 0000000000..f302cd6c3d
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
@@ -0,0 +1,43 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= controller_class_name %>ControllerTest < ActionController::TestCase
+ setup do
+ @<%= singular_table_name %> = <%= fixture_name %>(:one)
+<% if mountable_engine? -%>
+ @routes = Engine.routes
+<% end -%>
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ end
+
+ test "should create <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count') do
+ post :create, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ end
+
+ assert_response 201
+ end
+
+ test "should show <%= singular_table_name %>" do
+ get :show, params: { id: <%= "@#{singular_table_name}" %> }
+ assert_response :success
+ end
+
+ test "should update <%= singular_table_name %>" do
+ patch :update, params: { id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ assert_response 200
+ end
+
+ test "should destroy <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count', -1) do
+ delete :destroy, params: { id: <%= "@#{singular_table_name}" %> }
+ end
+
+ assert_response 204
+ end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
new file mode 100644
index 0000000000..50b98b2631
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
@@ -0,0 +1,53 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= controller_class_name %>ControllerTest < ActionController::TestCase
+ setup do
+ @<%= singular_table_name %> = <%= fixture_name %>(:one)
+<% if mountable_engine? -%>
+ @routes = Engine.routes
+<% end -%>
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ end
+
+ test "should get new" do
+ get :new
+ assert_response :success
+ end
+
+ test "should create <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count') do
+ post :create, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ end
+
+ assert_redirected_to <%= singular_table_name %>_path(<%= class_name %>.last)
+ end
+
+ test "should show <%= singular_table_name %>" do
+ get :show, params: { id: <%= "@#{singular_table_name}" %> }
+ assert_response :success
+ end
+
+ test "should get edit" do
+ get :edit, params: { id: <%= "@#{singular_table_name}" %> }
+ assert_response :success
+ end
+
+ test "should update <%= singular_table_name %>" do
+ patch :update, params: { id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ assert_redirected_to <%= singular_table_name %>_path(<%= "@#{singular_table_name}" %>)
+ end
+
+ test "should destroy <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count', -1) do
+ delete :destroy, params: { id: <%= "@#{singular_table_name}" %> }
+ end
+
+ assert_redirected_to <%= index_helper %>_path
+ end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/testing/assertions.rb b/railties/lib/rails/generators/testing/assertions.rb
new file mode 100644
index 0000000000..76758df86d
--- /dev/null
+++ b/railties/lib/rails/generators/testing/assertions.rb
@@ -0,0 +1,121 @@
+module Rails
+ module Generators
+ module Testing
+ module Assertions
+ # Asserts a given file exists. You need to supply an absolute path or a path relative
+ # to the configured destination:
+ #
+ # assert_file "config/environment.rb"
+ #
+ # You can also give extra arguments. If the argument is a regexp, it will check if the
+ # regular expression matches the given file content. If it's a string, it compares the
+ # file with the given string:
+ #
+ # assert_file "config/environment.rb", /initialize/
+ #
+ # Finally, when a block is given, it yields the file content:
+ #
+ # assert_file "app/controllers/products_controller.rb" do |controller|
+ # assert_instance_method :index, controller do |index|
+ # assert_match(/Product\.all/, index)
+ # end
+ # end
+ def assert_file(relative, *contents)
+ absolute = File.expand_path(relative, destination_root)
+ assert File.exist?(absolute), "Expected file #{relative.inspect} to exist, but does not"
+
+ read = File.read(absolute) if block_given? || !contents.empty?
+ yield read if block_given?
+
+ contents.each do |content|
+ case content
+ when String
+ assert_equal content, read
+ when Regexp
+ assert_match content, read
+ end
+ end
+ end
+ alias :assert_directory :assert_file
+
+ # Asserts a given file does not exist. You need to supply an absolute path or a
+ # path relative to the configured destination:
+ #
+ # assert_no_file "config/random.rb"
+ def assert_no_file(relative)
+ absolute = File.expand_path(relative, destination_root)
+ assert !File.exist?(absolute), "Expected file #{relative.inspect} to not exist, but does"
+ end
+ alias :assert_no_directory :assert_no_file
+
+ # Asserts a given migration exists. You need to supply an absolute path or a
+ # path relative to the configured destination:
+ #
+ # assert_migration "db/migrate/create_products.rb"
+ #
+ # This method manipulates the given path and tries to find any migration which
+ # matches the migration name. For example, the call above is converted to:
+ #
+ # assert_file "db/migrate/003_create_products.rb"
+ #
+ # Consequently, assert_migration accepts the same arguments has assert_file.
+ def assert_migration(relative, *contents, &block)
+ file_name = migration_file_name(relative)
+ assert file_name, "Expected migration #{relative} to exist, but was not found"
+ assert_file file_name, *contents, &block
+ end
+
+ # Asserts a given migration does not exist. You need to supply an absolute path or a
+ # path relative to the configured destination:
+ #
+ # assert_no_migration "db/migrate/create_products.rb"
+ def assert_no_migration(relative)
+ file_name = migration_file_name(relative)
+ assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}"
+ end
+
+ # Asserts the given class method exists in the given content. This method does not detect
+ # class methods inside (class << self), only class methods which starts with "self.".
+ # When a block is given, it yields the content of the method.
+ #
+ # assert_migration "db/migrate/create_products.rb" do |migration|
+ # assert_class_method :up, migration do |up|
+ # assert_match(/create_table/, up)
+ # end
+ # end
+ def assert_class_method(method, content, &block)
+ assert_instance_method "self.#{method}", content, &block
+ end
+
+ # Asserts the given method exists in the given content. When a block is given,
+ # it yields the content of the method.
+ #
+ # assert_file "app/controllers/products_controller.rb" do |controller|
+ # assert_instance_method :index, controller do |index|
+ # assert_match(/Product\.all/, index)
+ # end
+ # end
+ def assert_instance_method(method, content)
+ assert content =~ /(\s+)def #{method}(\(.+\))?(.*?)\n\1end/m, "Expected to have method #{method}"
+ yield $3.strip if block_given?
+ end
+ alias :assert_method :assert_instance_method
+
+ # Asserts the given attribute type gets translated to a field type
+ # properly:
+ #
+ # assert_field_type :date, :date_select
+ def assert_field_type(attribute_type, field_type)
+ assert_equal(field_type, create_generated_attribute(attribute_type).field_type)
+ end
+
+ # Asserts the given attribute type gets a proper default value:
+ #
+ # assert_field_default_value :string, "MyString"
+ def assert_field_default_value(attribute_type, value)
+ assert_equal(value, create_generated_attribute(attribute_type).default)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb
new file mode 100644
index 0000000000..94b5e52224
--- /dev/null
+++ b/railties/lib/rails/generators/testing/behaviour.rb
@@ -0,0 +1,110 @@
+require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/hash/reverse_merge'
+require 'active_support/core_ext/kernel/reporting'
+require 'active_support/testing/stream'
+require 'active_support/concern'
+require 'rails/generators'
+
+module Rails
+ module Generators
+ module Testing
+ module Behaviour
+ extend ActiveSupport::Concern
+ include ActiveSupport::Testing::Stream
+
+ included do
+ class_attribute :destination_root, :current_path, :generator_class, :default_arguments
+
+ # Generators frequently change the current path using +FileUtils.cd+.
+ # So we need to store the path at file load and revert back to it after each test.
+ self.current_path = File.expand_path(Dir.pwd)
+ self.default_arguments = []
+ end
+
+ module ClassMethods
+ # Sets which generator should be tested:
+ #
+ # tests AppGenerator
+ def tests(klass)
+ self.generator_class = klass
+ end
+
+ # Sets default arguments on generator invocation. This can be overwritten when
+ # invoking it.
+ #
+ # arguments %w(app_name --skip-active-record)
+ def arguments(array)
+ self.default_arguments = array
+ end
+
+ # Sets the destination of generator files:
+ #
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ def destination(path)
+ self.destination_root = path
+ end
+ end
+
+ # Runs the generator configured for this class. The first argument is an array like
+ # command line arguments:
+ #
+ # class AppGeneratorTest < Rails::Generators::TestCase
+ # tests AppGenerator
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ # setup :prepare_destination
+ #
+ # test "database.yml is not created when skipping Active Record" do
+ # run_generator %w(myapp --skip-active-record)
+ # assert_no_file "config/database.yml"
+ # end
+ # end
+ #
+ # You can provide a configuration hash as second argument. This method returns the output
+ # printed by the generator.
+ def run_generator(args=self.default_arguments, config={})
+ capture(:stdout) do
+ args += ['--skip-bundle'] unless args.include? '--dev'
+ self.generator_class.start(args, config.reverse_merge(destination_root: destination_root))
+ end
+ end
+
+ # Instantiate the generator.
+ def generator(args=self.default_arguments, options={}, config={})
+ @generator ||= self.generator_class.new(args, options, config.reverse_merge(destination_root: destination_root))
+ end
+
+ # Create a Rails::Generators::GeneratedAttribute by supplying the
+ # attribute type and, optionally, the attribute name:
+ #
+ # create_generated_attribute(:string, 'name')
+ def create_generated_attribute(attribute_type, name = 'test', index = nil)
+ Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(':'))
+ end
+
+ protected
+
+ def destination_root_is_set? # :nodoc:
+ raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
+ end
+
+ def ensure_current_path # :nodoc:
+ cd current_path
+ end
+
+ # Clears all files and directories in destination.
+ def prepare_destination
+ rm_rf(destination_root)
+ mkdir_p(destination_root)
+ end
+
+ def migration_file_name(relative) # :nodoc:
+ absolute = File.expand_path(relative, destination_root)
+ dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '')
+ Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
+ end
+
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/testing/setup_and_teardown.rb b/railties/lib/rails/generators/testing/setup_and_teardown.rb
new file mode 100644
index 0000000000..73102a283f
--- /dev/null
+++ b/railties/lib/rails/generators/testing/setup_and_teardown.rb
@@ -0,0 +1,18 @@
+module Rails
+ module Generators
+ module Testing
+ module SetupAndTeardown
+ def setup # :nodoc:
+ destination_root_is_set?
+ ensure_current_path
+ super
+ end
+
+ def teardown # :nodoc:
+ ensure_current_path
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
new file mode 100644
index 0000000000..5909446b66
--- /dev/null
+++ b/railties/lib/rails/info.rb
@@ -0,0 +1,102 @@
+require "cgi"
+
+module Rails
+ # This module helps build the runtime properties used to display in the
+ # Rails::InfoController responses. Including the active Rails version, Ruby
+ # version, Rack version, and so on.
+ module Info
+ mattr_accessor :properties
+ class << (@@properties = [])
+ def names
+ map(&:first)
+ end
+
+ def value_for(property_name)
+ if property = assoc(property_name)
+ property.last
+ end
+ end
+ end
+
+ class << self #:nodoc:
+ def property(name, value = nil)
+ value ||= yield
+ properties << [name, value] if value
+ rescue Exception
+ end
+
+ def to_s
+ column_width = properties.names.map(&:length).max
+ info = properties.map do |name, value|
+ value = value.join(", ") if value.is_a?(Array)
+ "%-#{column_width}s %s" % [name, value]
+ end
+ info.unshift "About your application's environment"
+ info * "\n"
+ end
+
+ alias inspect to_s
+
+ def to_html
+ '<table>'.tap do |table|
+ properties.each do |(name, value)|
+ table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
+ formatted_value = if value.kind_of?(Array)
+ "<ul>" + value.map { |v| "<li>#{CGI.escapeHTML(v.to_s)}</li>" }.join + "</ul>"
+ else
+ CGI.escapeHTML(value.to_s)
+ end
+ table << %(<td class="value">#{formatted_value}</td></tr>)
+ end
+ table << '</table>'
+ end
+ end
+ end
+
+ # The Rails version.
+ property 'Rails version' do
+ Rails.version.to_s
+ end
+
+ # The Ruby version and platform, e.g. "2.0.0-p247 (x86_64-darwin12.4.0)".
+ property 'Ruby version' do
+ "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
+ end
+
+ # The RubyGems version, if it's installed.
+ property 'RubyGems version' do
+ Gem::RubyGemsVersion
+ end
+
+ property 'Rack version' do
+ ::Rack.release
+ end
+
+ property 'JavaScript Runtime' do
+ ExecJS.runtime.name
+ end
+
+ property 'Middleware' do
+ Rails.configuration.middleware.map(&:inspect)
+ end
+
+ # The application's location on the filesystem.
+ property 'Application root' do
+ File.expand_path(Rails.root)
+ end
+
+ # The current Rails environment (development, test, or production).
+ property 'Environment' do
+ Rails.env
+ end
+
+ # The name of the database adapter for the current environment.
+ property 'Database adapter' do
+ ActiveRecord::Base.configurations[Rails.env]['adapter']
+ end
+
+ property 'Database schema version' do
+ ActiveRecord::Migrator.current_version rescue nil
+ end
+ end
+end
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
new file mode 100644
index 0000000000..778105c5f7
--- /dev/null
+++ b/railties/lib/rails/info_controller.rb
@@ -0,0 +1,44 @@
+require 'rails/application_controller'
+require 'action_dispatch/routing/inspector'
+
+class Rails::InfoController < Rails::ApplicationController # :nodoc:
+ prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
+ layout -> { request.xhr? ? false : 'application' }
+
+ before_action :require_local!
+
+ def index
+ redirect_to action: :routes
+ end
+
+ def properties
+ @info = Rails::Info.to_html
+ @page_title = 'Properties'
+ end
+
+ def routes
+ if path = params[:path]
+ path = URI.parser.escape path
+ normalized_path = with_leading_slash path
+ render json: {
+ exact: match_route {|it| it.match normalized_path },
+ fuzzy: match_route {|it| it.spec.to_s.match path }
+ }
+ else
+ @routes_inspector = ActionDispatch::Routing::RoutesInspector.new(_routes.routes)
+ @page_title = 'Routes'
+ end
+ end
+
+ private
+
+ def match_route
+ _routes.routes.select {|route|
+ yield route.path
+ }.map {|route| route.path.spec.to_s }
+ end
+
+ def with_leading_slash(path)
+ ('/' + path).squeeze('/')
+ end
+end
diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb
new file mode 100644
index 0000000000..1a0b6d1e1a
--- /dev/null
+++ b/railties/lib/rails/initializable.rb
@@ -0,0 +1,89 @@
+require 'tsort'
+
+module Rails
+ module Initializable
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+ end
+
+ class Initializer
+ attr_reader :name, :block
+
+ def initialize(name, context, options, &block)
+ options[:group] ||= :default
+ @name, @context, @options, @block = name, context, options, block
+ end
+
+ def before
+ @options[:before]
+ end
+
+ def after
+ @options[:after]
+ end
+
+ def belongs_to?(group)
+ @options[:group] == group || @options[:group] == :all
+ end
+
+ def run(*args)
+ @context.instance_exec(*args, &block)
+ end
+
+ def bind(context)
+ return self if @context
+ Initializer.new(@name, context, @options, &block)
+ end
+ end
+
+ class Collection < Array
+ include TSort
+
+ alias :tsort_each_node :each
+ def tsort_each_child(initializer, &block)
+ select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block)
+ end
+
+ def +(other)
+ Collection.new(to_a + other.to_a)
+ end
+ end
+
+ def run_initializers(group=:default, *args)
+ return if instance_variable_defined?(:@ran)
+ initializers.tsort_each do |initializer|
+ initializer.run(*args) if initializer.belongs_to?(group)
+ end
+ @ran = true
+ end
+
+ def initializers
+ @initializers ||= self.class.initializers_for(self)
+ end
+
+ module ClassMethods
+ def initializers
+ @initializers ||= Collection.new
+ end
+
+ def initializers_chain
+ initializers = Collection.new
+ ancestors.reverse_each do |klass|
+ next unless klass.respond_to?(:initializers)
+ initializers = initializers + klass.initializers
+ end
+ initializers
+ end
+
+ def initializers_for(binding)
+ Collection.new(initializers_chain.map { |i| i.bind(binding) })
+ end
+
+ def initializer(name, opts = {}, &blk)
+ raise ArgumentError, "A block must be passed when defining an initializer" unless blk
+ opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
+ initializers << Initializer.new(name, nil, opts, &blk)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
new file mode 100644
index 0000000000..6143cf2dd9
--- /dev/null
+++ b/railties/lib/rails/mailers_controller.rb
@@ -0,0 +1,79 @@
+require 'rails/application_controller'
+
+class Rails::MailersController < Rails::ApplicationController # :nodoc:
+ prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
+
+ before_action :require_local!, unless: :show_previews?
+ before_action :find_preview, only: :preview
+
+ def index
+ @previews = ActionMailer::Preview.all
+ @page_title = "Mailer Previews"
+ end
+
+ def preview
+ if params[:path] == @preview.preview_name
+ @page_title = "Mailer Previews for #{@preview.preview_name}"
+ render action: 'mailer'
+ else
+ @email_action = File.basename(params[:path])
+
+ if @preview.email_exists?(@email_action)
+ @email = @preview.call(@email_action)
+
+ if params[:part]
+ part_type = Mime::Type.lookup(params[:part])
+
+ if part = find_part(part_type)
+ response.content_type = part_type
+ render plain: part.respond_to?(:decoded) ? part.decoded : part
+ else
+ raise AbstractController::ActionNotFound, "Email part '#{part_type}' not found in #{@preview.name}##{@email_action}"
+ end
+ else
+ @part = find_preferred_part(request.format, Mime[:html], Mime[:text])
+ render action: 'email', layout: false, formats: %w[html]
+ end
+ else
+ raise AbstractController::ActionNotFound, "Email '#{@email_action}' not found in #{@preview.name}"
+ end
+ end
+ end
+
+ protected
+ def show_previews?
+ ActionMailer::Base.show_previews
+ end
+
+ def find_preview
+ candidates = []
+ params[:path].to_s.scan(%r{/|$}){ candidates << $` }
+ preview = candidates.detect{ |candidate| ActionMailer::Preview.exists?(candidate) }
+
+ if preview
+ @preview = ActionMailer::Preview.find(preview)
+ else
+ raise AbstractController::ActionNotFound, "Mailer preview '#{params[:path]}' not found"
+ end
+ end
+
+ def find_preferred_part(*formats)
+ formats.each do |format|
+ if part = @email.find_first_mime_type(format)
+ return part
+ end
+ end
+
+ if formats.any?{ |f| @email.mime_type == f }
+ @email
+ end
+ end
+
+ def find_part(format)
+ if part = @email.find_first_mime_type(format)
+ part
+ elsif @email.mime_type == format
+ @email
+ end
+ end
+end
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
new file mode 100644
index 0000000000..e47616a87f
--- /dev/null
+++ b/railties/lib/rails/paths.rb
@@ -0,0 +1,219 @@
+module Rails
+ module Paths
+ # This object is an extended hash that behaves as root of the <tt>Rails::Paths</tt> system.
+ # It allows you to collect information about how you want to structure your application
+ # paths by a Hash like API. It requires you to give a physical path on initialization.
+ #
+ # root = Root.new "/rails"
+ # root.add "app/controllers", eager_load: true
+ #
+ # The command above creates a new root object and adds "app/controllers" as a path.
+ # This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
+ #
+ # path = root["app/controllers"]
+ # path.eager_load? # => true
+ # path.is_a?(Rails::Paths::Path) # => true
+ #
+ # The +Path+ object is simply an enumerable and allows you to easily add extra paths:
+ #
+ # path.is_a?(Enumerable) # => true
+ # path.to_ary.inspect # => ["app/controllers"]
+ #
+ # path << "lib/controllers"
+ # path.to_ary.inspect # => ["app/controllers", "lib/controllers"]
+ #
+ # Notice that when you add a path using +add+, the path object created already
+ # contains the path with the same path value given to +add+. In some situations,
+ # you may not want this behavior, so you can give <tt>:with</tt> as option.
+ #
+ # root.add "config/routes", with: "config/routes.rb"
+ # root["config/routes"].inspect # => ["config/routes.rb"]
+ #
+ # The +add+ method accepts the following options as arguments:
+ # eager_load, autoload, autoload_once and glob.
+ #
+ # Finally, the +Path+ object also provides a few helpers:
+ #
+ # root = Root.new "/rails"
+ # root.add "app/controllers"
+ #
+ # root["app/controllers"].expanded # => ["/rails/app/controllers"]
+ # root["app/controllers"].existent # => ["/rails/app/controllers"]
+ #
+ # Check the <tt>Rails::Paths::Path</tt> documentation for more information.
+ class Root
+ attr_accessor :path
+
+ def initialize(path)
+ @current = nil
+ @path = path
+ @root = {}
+ end
+
+ def []=(path, value)
+ glob = self[path] ? self[path].glob : nil
+ add(path, with: value, glob: glob)
+ end
+
+ def add(path, options = {})
+ with = Array(options.fetch(:with, path))
+ @root[path] = Path.new(self, path, with, options)
+ end
+
+ def [](path)
+ @root[path]
+ end
+
+ def values
+ @root.values
+ end
+
+ def keys
+ @root.keys
+ end
+
+ def values_at(*list)
+ @root.values_at(*list)
+ end
+
+ def all_paths
+ values.tap(&:uniq!)
+ end
+
+ def autoload_once
+ filter_by(&:autoload_once?)
+ end
+
+ def eager_load
+ filter_by(&:eager_load?)
+ end
+
+ def autoload_paths
+ filter_by(&:autoload?)
+ end
+
+ def load_paths
+ filter_by(&:load_path?)
+ end
+
+ private
+
+ def filter_by(&block)
+ all_paths.find_all(&block).flat_map { |path|
+ paths = path.existent
+ paths - path.children.flat_map { |p| yield(p) ? [] : p.existent }
+ }.uniq
+ end
+ end
+
+ class Path
+ include Enumerable
+
+ attr_accessor :glob
+
+ def initialize(root, current, paths, options = {})
+ @paths = paths
+ @current = current
+ @root = root
+ @glob = options[:glob]
+
+ options[:autoload_once] ? autoload_once! : skip_autoload_once!
+ options[:eager_load] ? eager_load! : skip_eager_load!
+ options[:autoload] ? autoload! : skip_autoload!
+ options[:load_path] ? load_path! : skip_load_path!
+ end
+
+ def absolute_current # :nodoc:
+ File.expand_path(@current, @root.path)
+ end
+
+ def children
+ keys = @root.keys.find_all { |k|
+ k.start_with?(@current) && k != @current
+ }
+ @root.values_at(*keys.sort)
+ end
+
+ def first
+ expanded.first
+ end
+
+ def last
+ expanded.last
+ end
+
+ %w(autoload_once eager_load autoload load_path).each do |m|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{m}! # def eager_load!
+ @#{m} = true # @eager_load = true
+ end # end
+ #
+ def skip_#{m}! # def skip_eager_load!
+ @#{m} = false # @eager_load = false
+ end # end
+ #
+ def #{m}? # def eager_load?
+ @#{m} # @eager_load
+ end # end
+ RUBY
+ end
+
+ def each(&block)
+ @paths.each(&block)
+ end
+
+ def <<(path)
+ @paths << path
+ end
+ alias :push :<<
+
+ def concat(paths)
+ @paths.concat paths
+ end
+
+ def unshift(*paths)
+ @paths.unshift(*paths)
+ end
+
+ def to_ary
+ @paths
+ end
+
+ def extensions # :nodoc:
+ $1.split(',') if @glob =~ /\{([\S]+)\}/
+ end
+
+ # Expands all paths against the root and return all unique values.
+ def expanded
+ raise "You need to set a path root" unless @root.path
+ result = []
+
+ each do |p|
+ path = File.expand_path(p, @root.path)
+
+ if @glob && File.directory?(path)
+ Dir.chdir(path) do
+ result.concat(Dir.glob(@glob).map { |file| File.join path, file }.sort)
+ end
+ else
+ result << path
+ end
+ end
+
+ result.uniq!
+ result
+ end
+
+ # Returns all expanded paths but only if they exist in the filesystem.
+ def existent
+ expanded.select { |f| File.exist?(f) }
+ end
+
+ def existent_directories
+ expanded.select { |d| File.directory?(d) }
+ end
+
+ alias to_a expanded
+ end
+ end
+end
diff --git a/railties/lib/rails/rack.rb b/railties/lib/rails/rack.rb
new file mode 100644
index 0000000000..a4c4527a72
--- /dev/null
+++ b/railties/lib/rails/rack.rb
@@ -0,0 +1,5 @@
+module Rails
+ module Rack
+ autoload :Logger, "rails/rack/logger"
+ end
+end
diff --git a/railties/lib/rails/rack/debugger.rb b/railties/lib/rails/rack/debugger.rb
new file mode 100644
index 0000000000..1fde3db070
--- /dev/null
+++ b/railties/lib/rails/rack/debugger.rb
@@ -0,0 +1,3 @@
+require 'active_support/deprecation'
+
+ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
new file mode 100644
index 0000000000..12676b18bc
--- /dev/null
+++ b/railties/lib/rails/rack/logger.rb
@@ -0,0 +1,90 @@
+require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/object/blank'
+require 'active_support/log_subscriber'
+require 'action_dispatch/http/request'
+require 'rack/body_proxy'
+
+module Rails
+ module Rack
+ # Sets log tags, logs the request, calls the app, and flushes the logs.
+ #
+ # Log tags (+taggers+) can be an Array containing: methods that the +request+
+ # object responds to, objects that respond to +to_s+ or Proc objects that accept
+ # an instance of the +request+ object.
+ class Logger < ActiveSupport::LogSubscriber
+ def initialize(app, taggers = nil)
+ @app = app
+ @taggers = taggers || []
+ end
+
+ def call(env)
+ request = ActionDispatch::Request.new(env)
+
+ if logger.respond_to?(:tagged)
+ logger.tagged(compute_tags(request)) { call_app(request, env) }
+ else
+ call_app(request, env)
+ end
+ end
+
+ protected
+
+ def call_app(request, env)
+ # Put some space between requests in development logs.
+ if development?
+ logger.debug ''
+ logger.debug ''
+ end
+
+ instrumenter = ActiveSupport::Notifications.instrumenter
+ instrumenter.start 'request.action_dispatch', request: request
+ logger.info { started_request_message(request) }
+ resp = @app.call(env)
+ resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
+ resp
+ rescue Exception
+ finish(request)
+ raise
+ ensure
+ ActiveSupport::LogSubscriber.flush_all!
+ end
+
+ # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
+ def started_request_message(request)
+ 'Started %s "%s" for %s at %s' % [
+ request.request_method,
+ request.filtered_path,
+ request.ip,
+ Time.now.to_default_s ]
+ end
+
+ def compute_tags(request)
+ @taggers.collect do |tag|
+ case tag
+ when Proc
+ tag.call(request)
+ when Symbol
+ request.send(tag)
+ else
+ tag
+ end
+ end
+ end
+
+ private
+
+ def finish(request)
+ instrumenter = ActiveSupport::Notifications.instrumenter
+ instrumenter.finish 'request.action_dispatch', request: request
+ end
+
+ def development?
+ Rails.env.development?
+ end
+
+ def logger
+ Rails.logger
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
new file mode 100644
index 0000000000..8c24d1d56d
--- /dev/null
+++ b/railties/lib/rails/railtie.rb
@@ -0,0 +1,250 @@
+require 'rails/initializable'
+require 'rails/configuration'
+require 'active_support/inflector'
+require 'active_support/core_ext/module/introspection'
+require 'active_support/core_ext/module/delegation'
+
+module Rails
+ # Railtie is the core of the Rails framework and provides several hooks to extend
+ # Rails and/or modify the initialization process.
+ #
+ # Every major component of Rails (Action Mailer, Action Controller,
+ # Action View and Active Record) is a Railtie. Each of
+ # them is responsible for their own initialization. This makes Rails itself
+ # absent of any component hooks, allowing other components to be used in
+ # place of any of the Rails defaults.
+ #
+ # Developing a Rails extension does _not_ require any implementation of
+ # Railtie, but if you need to interact with the Rails framework during
+ # or after boot, then Railtie is needed.
+ #
+ # For example, an extension doing any of the following would require Railtie:
+ #
+ # * creating initializers
+ # * configuring a Rails framework for the application, like setting a generator
+ # * adding <tt>config.*</tt> keys to the environment
+ # * setting up a subscriber with ActiveSupport::Notifications
+ # * adding rake tasks
+ #
+ # == Creating your Railtie
+ #
+ # To extend Rails using Railtie, create a Railtie class which inherits
+ # from Rails::Railtie within your extension's namespace. This class must be
+ # loaded during the Rails boot process.
+ #
+ # The following example demonstrates an extension which can be used with or without Rails.
+ #
+ # # lib/my_gem/railtie.rb
+ # module MyGem
+ # class Railtie < Rails::Railtie
+ # end
+ # end
+ #
+ # # lib/my_gem.rb
+ # require 'my_gem/railtie' if defined?(Rails)
+ #
+ # == Initializers
+ #
+ # To add an initialization step from your Railtie to Rails boot process, you just need
+ # to create an initializer block:
+ #
+ # class MyRailtie < Rails::Railtie
+ # initializer "my_railtie.configure_rails_initialization" do
+ # # some initialization behavior
+ # end
+ # end
+ #
+ # If specified, the block can also receive the application object, in case you
+ # need to access some application specific configuration, like middleware:
+ #
+ # class MyRailtie < Rails::Railtie
+ # initializer "my_railtie.configure_rails_initialization" do |app|
+ # app.middleware.use MyRailtie::Middleware
+ # end
+ # end
+ #
+ # Finally, you can also pass <tt>:before</tt> and <tt>:after</tt> as option to initializer,
+ # in case you want to couple it with a specific step in the initialization process.
+ #
+ # == Configuration
+ #
+ # Inside the Railtie class, you can access a config object which contains configuration
+ # shared by all railties and the application:
+ #
+ # class MyRailtie < Rails::Railtie
+ # # Customize the ORM
+ # config.app_generators.orm :my_railtie_orm
+ #
+ # # Add a to_prepare block which is executed once in production
+ # # and before each request in development
+ # config.to_prepare do
+ # MyRailtie.setup!
+ # end
+ # end
+ #
+ # == Loading rake tasks and generators
+ #
+ # If your railtie has rake tasks, you can tell Rails to load them through the method
+ # rake_tasks:
+ #
+ # class MyRailtie < Rails::Railtie
+ # rake_tasks do
+ # load "path/to/my_railtie.tasks"
+ # end
+ # end
+ #
+ # By default, Rails loads generators from your load path. However, if you want to place
+ # your generators at a different location, you can specify in your Railtie a block which
+ # will load them during normal generators lookup:
+ #
+ # class MyRailtie < Rails::Railtie
+ # generators do
+ # require "path/to/my_railtie_generator"
+ # end
+ # end
+ #
+ # == Application and Engine
+ #
+ # A Rails::Engine is nothing more than a Railtie with some initializers already set.
+ # And since Rails::Application is an engine, the same configuration described here
+ # can be used in both.
+ #
+ # Be sure to look at the documentation of those specific classes for more information.
+ #
+ class Railtie
+ autoload :Configuration, "rails/railtie/configuration"
+
+ include Initializable
+
+ ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application)
+
+ class << self
+ private :new
+ delegate :config, to: :instance
+
+ def subclasses
+ @subclasses ||= []
+ end
+
+ def inherited(base)
+ unless base.abstract_railtie?
+ subclasses << base
+ end
+ end
+
+ def rake_tasks(&blk)
+ @rake_tasks ||= []
+ @rake_tasks << blk if blk
+ @rake_tasks
+ end
+
+ def console(&blk)
+ @load_console ||= []
+ @load_console << blk if blk
+ @load_console
+ end
+
+ def runner(&blk)
+ @load_runner ||= []
+ @load_runner << blk if blk
+ @load_runner
+ end
+
+ def generators(&blk)
+ @generators ||= []
+ @generators << blk if blk
+ @generators
+ end
+
+ def abstract_railtie?
+ ABSTRACT_RAILTIES.include?(name)
+ end
+
+ def railtie_name(name = nil)
+ @railtie_name = name.to_s if name
+ @railtie_name ||= generate_railtie_name(self.name)
+ end
+
+ # Since Rails::Railtie cannot be instantiated, any methods that call
+ # +instance+ are intended to be called only on subclasses of a Railtie.
+ def instance
+ @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.
+ def configure(&block)
+ instance.configure(&block)
+ end
+
+ protected
+ def generate_railtie_name(string)
+ ActiveSupport::Inflector.underscore(string).tr("/", "_")
+ 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)
+ if instance.respond_to?(name)
+ instance.public_send(name, *args, &block)
+ else
+ super
+ end
+ end
+ end
+
+ delegate :railtie_name, to: :class
+
+ def initialize
+ if self.class.abstract_railtie?
+ raise "#{self.class.name} is abstract, you cannot instantiate it directly."
+ end
+ end
+
+ def configure(&block)
+ instance_eval(&block)
+ end
+
+ def config
+ @config ||= Railtie::Configuration.new
+ end
+
+ def railtie_namespace
+ @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
+ end
+
+ protected
+
+ def run_console_blocks(app) #:nodoc:
+ each_registered_block(:console) { |block| block.call(app) }
+ end
+
+ def run_generators_blocks(app) #:nodoc:
+ each_registered_block(:generators) { |block| block.call(app) }
+ end
+
+ def run_runner_blocks(app) #:nodoc:
+ each_registered_block(:runner) { |block| block.call(app) }
+ end
+
+ def run_tasks_blocks(app) #:nodoc:
+ extend Rake::DSL
+ each_registered_block(:rake_tasks) { |block| instance_exec(app, &block) }
+ end
+
+ private
+
+ def each_registered_block(type, &block)
+ klass = self.class
+ while klass.respond_to?(type)
+ klass.public_send(type).each(&block)
+ klass = klass.superclass
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb
new file mode 100644
index 0000000000..1572af0b2a
--- /dev/null
+++ b/railties/lib/rails/railtie/configurable.rb
@@ -0,0 +1,35 @@
+require 'active_support/concern'
+
+module Rails
+ class Railtie
+ module Configurable
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ delegate :config, to: :instance
+
+ def inherited(base)
+ raise "You cannot inherit from a #{self.superclass.name} child"
+ end
+
+ def instance
+ @instance ||= new
+ end
+
+ def respond_to?(*args)
+ super || instance.respond_to?(*args)
+ end
+
+ def configure(&block)
+ class_eval(&block)
+ end
+
+ protected
+
+ def method_missing(*args, &block)
+ instance.send(*args, &block)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
new file mode 100644
index 0000000000..eb3b2d8ef4
--- /dev/null
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -0,0 +1,100 @@
+require 'rails/configuration'
+
+module Rails
+ class Railtie
+ class Configuration
+ def initialize
+ @@options ||= {}
+ end
+
+ # Expose the eager_load_namespaces at "module" level for convenience.
+ def self.eager_load_namespaces #:nodoc:
+ @@eager_load_namespaces ||= []
+ end
+
+ # All namespaces that are eager loaded
+ def eager_load_namespaces
+ @@eager_load_namespaces ||= []
+ end
+
+ # Add files that should be watched for change.
+ def watchable_files
+ @@watchable_files ||= []
+ end
+
+ # Add directories that should be watched for change.
+ # The key of the hashes should be directories and the values should
+ # be an array of extensions to match in each directory.
+ def watchable_dirs
+ @@watchable_dirs ||= {}
+ end
+
+ # This allows you to modify the application's middlewares from Engines.
+ #
+ # All operations you run on the app_middleware will be replayed on the
+ # application once it is defined and the default_middlewares are
+ # created
+ def app_middleware
+ @@app_middleware ||= Rails::Configuration::MiddlewareStackProxy.new
+ end
+
+ # This allows you to modify application's generators from Railties.
+ #
+ # Values set on app_generators will become defaults for application, unless
+ # application overwrites them.
+ def app_generators
+ @@app_generators ||= Rails::Configuration::Generators.new
+ yield(@@app_generators) if block_given?
+ @@app_generators
+ end
+
+ # First configurable block to run. Called before any initializers are run.
+ def before_configuration(&block)
+ ActiveSupport.on_load(:before_configuration, yield: true, &block)
+ end
+
+ # Third configurable block to run. Does not run if +config.cache_classes+
+ # set to false.
+ def before_eager_load(&block)
+ ActiveSupport.on_load(:before_eager_load, yield: true, &block)
+ end
+
+ # Second configurable block to run. Called before frameworks initialize.
+ def before_initialize(&block)
+ ActiveSupport.on_load(:before_initialize, yield: true, &block)
+ end
+
+ # Last configurable block to run. Called after frameworks initialize.
+ def after_initialize(&block)
+ ActiveSupport.on_load(:after_initialize, yield: true, &block)
+ end
+
+ # Array of callbacks defined by #to_prepare.
+ def to_prepare_blocks
+ @@to_prepare_blocks ||= []
+ end
+
+ # Defines generic callbacks to run before #after_initialize. Useful for
+ # Rails::Railtie subclasses.
+ def to_prepare(&blk)
+ to_prepare_blocks << blk if blk
+ end
+
+ def respond_to?(name, include_private = false)
+ super || @@options.key?(name.to_sym)
+ end
+
+ private
+
+ def method_missing(name, *args, &blk)
+ if name.to_s =~ /=$/
+ @@options[$`.to_sym] = args.first
+ elsif @@options.key?(name)
+ @@options[name]
+ else
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
new file mode 100644
index 0000000000..67a19d8a94
--- /dev/null
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -0,0 +1,13 @@
+if RUBY_VERSION < '2.2.2' && RUBY_ENGINE == 'ruby'
+ desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
+ abort <<-end_message
+
+ Rails 5 requires Ruby 2.2.2 or newer.
+
+ You're running
+ #{desc}
+
+ Please upgrade to Ruby 2.2.2 or newer to continue.
+
+ end_message
+end
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
new file mode 100644
index 0000000000..8dd87b6cc5
--- /dev/null
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -0,0 +1,133 @@
+# Implements the logic behind the rake tasks for annotations like
+#
+# rake notes
+# rake notes:optimize
+#
+# and friends. See <tt>rake -T notes</tt> and <tt>railties/lib/rails/tasks/annotations.rake</tt>.
+#
+# Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
+# represent the line where the annotation lives, its tag, and its text. Note
+# the filename is not stored.
+#
+# Annotations are looked for in comments and modulus whitespace they have to
+# start with the tag optionally followed by a colon. Everything up to the end
+# of the line (or closing ERB comment tag) is considered to be their text.
+class SourceAnnotationExtractor
+ class Annotation < Struct.new(:line, :tag, :text)
+ def self.directories
+ @@directories ||= %w(app config db lib test) + (ENV['SOURCE_ANNOTATION_DIRECTORIES'] || '').split(',')
+ end
+
+ def self.extensions
+ @@extensions ||= {}
+ end
+
+ # Registers new Annotations File Extensions
+ # SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
+ def self.register_extensions(*exts, &block)
+ extensions[/\.(#{exts.join("|")})$/] = block
+ end
+
+ register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ }
+ register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
+ register_extensions("erb") { |tag| /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/ }
+
+ # Returns a representation of the annotation that looks like this:
+ #
+ # [126] [TODO] This algorithm is simple and clearly correct, make it faster.
+ #
+ # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
+ # Otherwise the string contains just line and text.
+ def to_s(options={})
+ s = "[#{line.to_s.rjust(options[:indent])}] "
+ s << "[#{tag}] " if options[:tag]
+ s << text
+ end
+ end
+
+ # Prints all annotations with tag +tag+ under the root directories +app+,
+ # +config+, +db+, +lib+, and +test+ (recursively).
+ #
+ # Additional directories may be added using a comma-delimited list set using
+ # <tt>ENV['SOURCE_ANNOTATION_DIRECTORIES']</tt>.
+ #
+ # Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+.
+ #
+ # SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
+ #
+ # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+.
+ #
+ # See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
+ #
+ # This class method is the single entry point for the rake tasks.
+ def self.enumerate(tag, options={})
+ extractor = new(tag)
+ dirs = options.delete(:dirs) || Annotation.directories
+ extractor.display(extractor.find(dirs), options)
+ end
+
+ attr_reader :tag
+
+ def initialize(tag)
+ @tag = tag
+ end
+
+ # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
+ # with their annotations.
+ def find(dirs)
+ dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
+ end
+
+ # Returns a hash that maps filenames under +dir+ (recursively) to arrays
+ # with their annotations. Only files with annotations are included. Files
+ # with extension +.builder+, +.rb+, +.rake+, +.yml+, +.yaml+, +.ruby+,
+ # +.css+, +.js+ and +.erb+ are taken into account.
+ def find_in(dir)
+ results = {}
+
+ Dir.glob("#{dir}/*") do |item|
+ next if File.basename(item)[0] == ?.
+
+ if File.directory?(item)
+ results.update(find_in(item))
+ else
+ extension = Annotation.extensions.detect do |regexp, _block|
+ regexp.match(item)
+ end
+
+ if extension
+ pattern = extension.last.call(tag)
+ results.update(extract_annotations_from(item, pattern)) if pattern
+ end
+ end
+ end
+
+ results
+ end
+
+ # If +file+ is the filename of a file that contains annotations this method returns
+ # a hash with a single entry that maps +file+ to an array of its annotations.
+ # Otherwise it returns an empty hash.
+ def extract_annotations_from(file, pattern)
+ lineno = 0
+ result = File.readlines(file).inject([]) do |list, line|
+ lineno += 1
+ next list unless line =~ pattern
+ list << Annotation.new(lineno, $1, $2)
+ end
+ result.empty? ? {} : { file => result }
+ end
+
+ # Prints the mapping from filenames to annotations in +results+ ordered by filename.
+ # The +options+ hash is passed to each annotation's +to_s+.
+ def display(results, options={})
+ options[:indent] = results.flat_map { |f, a| a.map(&:line) }.max.to_s.size
+ results.keys.sort.each do |file|
+ puts "#{file}:"
+ results[file].each do |note|
+ puts " * #{note.to_s(options)}"
+ end
+ puts
+ end
+ end
+end
diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb
new file mode 100644
index 0000000000..d60eaf6f4f
--- /dev/null
+++ b/railties/lib/rails/tasks.rb
@@ -0,0 +1,18 @@
+require 'rake'
+
+# Load Rails Rakefile extensions
+%w(
+ annotations
+ framework
+ initializers
+ log
+ middleware
+ misc
+ restart
+ routes
+ tmp
+).tap { |arr|
+ arr << 'statistics' if Rake.application.current_scope.empty?
+}.each do |task|
+ load "rails/tasks/#{task}.rake"
+end
diff --git a/railties/lib/rails/tasks/annotations.rake b/railties/lib/rails/tasks/annotations.rake
new file mode 100644
index 0000000000..386ecf44be
--- /dev/null
+++ b/railties/lib/rails/tasks/annotations.rake
@@ -0,0 +1,20 @@
+require 'rails/source_annotation_extractor'
+
+desc "Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)"
+task :notes do
+ SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", tag: true
+end
+
+namespace :notes do
+ ["OPTIMIZE", "FIXME", "TODO"].each do |annotation|
+ # desc "Enumerate all #{annotation} annotations"
+ task annotation.downcase.intern do
+ SourceAnnotationExtractor.enumerate annotation
+ end
+ end
+
+ desc "Enumerate a custom annotation, specify with ANNOTATION=CUSTOM"
+ task :custom do
+ SourceAnnotationExtractor.enumerate ENV['ANNOTATION']
+ end
+end \ No newline at end of file
diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake
new file mode 100644
index 0000000000..c51524f8f6
--- /dev/null
+++ b/railties/lib/rails/tasks/engine.rake
@@ -0,0 +1,72 @@
+task "load_app" do
+ namespace :app do
+ load APP_RAKEFILE
+ end
+ task :environment => "app:environment"
+
+ if !defined?(ENGINE_PATH) || !ENGINE_PATH
+ ENGINE_PATH = find_engine_path(APP_RAKEFILE)
+ end
+end
+
+def app_task(name)
+ task name => [:load_app, "app:db:#{name}"]
+end
+
+namespace :db do
+ app_task "reset"
+
+ desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
+ app_task "migrate"
+ app_task "migrate:up"
+ app_task "migrate:down"
+ app_task "migrate:redo"
+ app_task "migrate:reset"
+
+ desc "Display status of migrations"
+ app_task "migrate:status"
+
+ desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)'
+ app_task "create"
+ app_task "create:all"
+
+ desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)'
+ app_task "drop"
+ app_task "drop:all"
+
+ desc "Load fixtures into the current environment's database."
+ app_task "fixtures:load"
+
+ desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)."
+ app_task "rollback"
+
+ desc "Create a db/schema.rb file that can be portably used against any DB supported by Active Record"
+ app_task "schema:dump"
+
+ desc "Load a schema.rb file into the database"
+ app_task "schema:load"
+
+ desc "Load the seed data from db/seeds.rb"
+ app_task "seed"
+
+ desc "Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)"
+ app_task "setup"
+
+ desc "Dump the database structure to an SQL file"
+ app_task "structure:dump"
+
+ desc "Retrieves the current schema version number"
+ app_task "version"
+end
+
+def find_engine_path(path)
+ return File.expand_path(Dir.pwd) if path == "/"
+
+ if Rails::Engine.find(path)
+ path
+ else
+ find_engine_path(File.expand_path('..', path))
+ end
+end
+
+Rake.application.invoke_task(:load_app)
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
new file mode 100644
index 0000000000..904b9d9ad6
--- /dev/null
+++ b/railties/lib/rails/tasks/framework.rake
@@ -0,0 +1,68 @@
+namespace :rails do
+ desc "Update configs and some other initially generated files (or use just update:configs or update:bin)"
+ task update: [ "update:configs", "update:bin" ]
+
+ desc "Applies the template supplied by LOCATION=(/path/to/template) or URL"
+ task template: :environment do
+ template = ENV["LOCATION"]
+ raise "No LOCATION value given. Please set LOCATION either as path to a file or a URL" if template.blank?
+ template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://}
+ require 'rails/generators'
+ require 'rails/generators/rails/app/app_generator'
+ generator = Rails::Generators::AppGenerator.new [Rails.root], {}, destination_root: Rails.root
+ generator.apply template, verbose: false
+ end
+
+ namespace :templates do
+ # desc "Copy all the templates from rails to the application directory for customization. Already existing local copies will be overwritten"
+ task :copy do
+ generators_lib = File.expand_path("../../generators", __FILE__)
+ project_templates = "#{Rails.root}/lib/templates"
+
+ default_templates = { "erb" => %w{controller mailer scaffold},
+ "rails" => %w{controller helper scaffold_controller assets} }
+
+ default_templates.each do |type, names|
+ local_template_type_dir = File.join(project_templates, type)
+ FileUtils.mkdir_p local_template_type_dir
+
+ names.each do |name|
+ dst_name = File.join(local_template_type_dir, name)
+ src_name = File.join(generators_lib, type, name, "templates")
+ FileUtils.cp_r src_name, dst_name
+ end
+ end
+ end
+ end
+
+ namespace :update do
+ class RailsUpdate
+ def self.invoke_from_app_generator(method)
+ app_generator.send(method)
+ end
+
+ def self.app_generator
+ @app_generator ||= begin
+ require 'rails/generators'
+ require 'rails/generators/rails/app/app_generator'
+ gen = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true },
+ destination_root: Rails.root
+ File.exist?(Rails.root.join("config", "application.rb")) ?
+ gen.send(:app_const) : gen.send(:valid_const?)
+ gen
+ end
+ end
+ end
+
+ # desc "Update config/boot.rb from your current rails install"
+ task :configs do
+ RailsUpdate.invoke_from_app_generator :create_boot_file
+ RailsUpdate.invoke_from_app_generator :update_config_files
+ end
+
+ # desc "Adds new executables to the application bin/ directory"
+ task :bin do
+ RailsUpdate.invoke_from_app_generator :create_bin_files
+ end
+ end
+end
diff --git a/railties/lib/rails/tasks/initializers.rake b/railties/lib/rails/tasks/initializers.rake
new file mode 100644
index 0000000000..2968b5cb53
--- /dev/null
+++ b/railties/lib/rails/tasks/initializers.rake
@@ -0,0 +1,6 @@
+desc "Print out all defined initializers in the order they are invoked by Rails."
+task initializers: :environment do
+ Rails.application.initializers.tsort_each do |initializer|
+ puts initializer.name
+ end
+end
diff --git a/railties/lib/rails/tasks/log.rake b/railties/lib/rails/tasks/log.rake
new file mode 100644
index 0000000000..877f175ef3
--- /dev/null
+++ b/railties/lib/rails/tasks/log.rake
@@ -0,0 +1,23 @@
+namespace :log do
+ desc "Truncates all *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)"
+ task :clear do
+ log_files.each do |file|
+ clear_log_file(file)
+ end
+ end
+
+ def log_files
+ if ENV['LOGS']
+ ENV['LOGS'].split(',')
+ .map { |file| "log/#{file.strip}.log" }
+ .select { |file| File.exist?(file) }
+ else
+ FileList["log/*.log"]
+ end
+ end
+
+ def clear_log_file(file)
+ f = File.open(file, "w")
+ f.close
+ end
+end
diff --git a/railties/lib/rails/tasks/middleware.rake b/railties/lib/rails/tasks/middleware.rake
new file mode 100644
index 0000000000..31e961b483
--- /dev/null
+++ b/railties/lib/rails/tasks/middleware.rake
@@ -0,0 +1,7 @@
+desc 'Prints out your Rack middleware stack'
+task middleware: :environment do
+ Rails.configuration.middleware.each do |middleware|
+ puts "use #{middleware.inspect}"
+ end
+ puts "run #{Rails.application.class.name}.routes"
+end
diff --git a/railties/lib/rails/tasks/misc.rake b/railties/lib/rails/tasks/misc.rake
new file mode 100644
index 0000000000..4195106961
--- /dev/null
+++ b/railties/lib/rails/tasks/misc.rake
@@ -0,0 +1,60 @@
+desc 'Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions).'
+task :secret do
+ require 'securerandom'
+ puts SecureRandom.hex(64)
+end
+
+desc 'List versions of all Rails frameworks and the environment'
+task about: :environment do
+ puts Rails::Info
+end
+
+namespace :time do
+ namespace :zones do
+ desc 'Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6'
+ task :all do
+ build_time_zone_list(:all)
+ end
+
+ # desc 'Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
+ task :us do
+ build_time_zone_list(:us_zones)
+ end
+
+ # desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time'
+ task :local do
+ require 'active_support'
+ require 'active_support/time'
+ jan_offset = Time.now.beginning_of_year.utc_offset
+ jul_offset = Time.now.beginning_of_year.change(month: 7).utc_offset
+ offset = jan_offset < jul_offset ? jan_offset : jul_offset
+ build_time_zone_list(:all, offset)
+ end
+
+ # to find UTC -06:00 zones, OFFSET can be set to either -6, -6:00 or 21600
+ def build_time_zone_list(method, offset = ENV['OFFSET'])
+ require 'active_support'
+ require 'active_support/time'
+ if offset
+ offset = if offset.to_s.match(/(\+|-)?(\d+):(\d+)/)
+ sign = $1 == '-' ? -1 : 1
+ hours, minutes = $2.to_f, $3.to_f
+ ((hours * 3600) + (minutes.to_f * 60)) * sign
+ elsif offset.to_f.abs <= 13
+ offset.to_f * 3600
+ else
+ offset.to_f
+ end
+ end
+ previous_offset = nil
+ ActiveSupport::TimeZone.__send__(method).each do |zone|
+ if offset.nil? || offset == zone.utc_offset
+ puts "\n* UTC #{zone.formatted_offset} *" unless zone.utc_offset == previous_offset
+ puts zone.name
+ previous_offset = zone.utc_offset
+ end
+ end
+ puts "\n"
+ end
+ end
+end
diff --git a/railties/lib/rails/tasks/restart.rake b/railties/lib/rails/tasks/restart.rake
new file mode 100644
index 0000000000..f36c86d81b
--- /dev/null
+++ b/railties/lib/rails/tasks/restart.rake
@@ -0,0 +1,5 @@
+desc "Restart app by touching tmp/restart.txt"
+task :restart do
+ FileUtils.mkdir_p('tmp')
+ FileUtils.touch('tmp/restart.txt')
+end
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
new file mode 100644
index 0000000000..1815c2fdc7
--- /dev/null
+++ b/railties/lib/rails/tasks/routes.rake
@@ -0,0 +1,7 @@
+desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
+task routes: :environment do
+ all_routes = Rails.application.routes.routes
+ require 'action_dispatch/routing/inspector'
+ inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
+ puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, ENV['CONTROLLER'])
+end
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
new file mode 100644
index 0000000000..a919d36939
--- /dev/null
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -0,0 +1,28 @@
+# While global constants are bad, many 3rd party tools depend on this one (e.g
+# rspec-rails & cucumber-rails). So a deprecation warning is needed if we want
+# to remove it.
+STATS_DIRECTORIES = [
+ %w(Controllers app/controllers),
+ %w(Helpers app/helpers),
+ %w(Jobs app/jobs),
+ %w(Models app/models),
+ %w(Mailers app/mailers),
+ %w(Javascripts app/assets/javascripts),
+ %w(Libraries lib/),
+ %w(Tasks lib/tasks),
+ %w(APIs app/apis),
+ %w(Controller\ tests test/controllers),
+ %w(Helper\ tests test/helpers),
+ %w(Model\ tests test/models),
+ %w(Mailer\ tests test/mailers),
+ %w(Job\ tests test/jobs),
+ %w(Integration\ tests test/integration),
+].collect do |name, dir|
+ [ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
+end.select { |name, dir| File.directory?(dir) }
+
+desc "Report code statistics (KLOCs, etc) from the application or engine"
+task :stats do
+ require 'rails/code_statistics'
+ CodeStatistics.new(*STATS_DIRECTORIES).to_s
+end
diff --git a/railties/lib/rails/tasks/tmp.rake b/railties/lib/rails/tasks/tmp.rake
new file mode 100644
index 0000000000..9162ef234a
--- /dev/null
+++ b/railties/lib/rails/tasks/tmp.rake
@@ -0,0 +1,37 @@
+namespace :tmp do
+ desc "Clear cache and socket files from tmp/ (narrow w/ tmp:cache:clear, tmp:sockets:clear)"
+ task clear: ["tmp:cache:clear", "tmp:sockets:clear"]
+
+ tmp_dirs = [ 'tmp/cache',
+ 'tmp/sockets',
+ 'tmp/pids',
+ 'tmp/cache/assets/development',
+ 'tmp/cache/assets/test',
+ 'tmp/cache/assets/production' ]
+
+ tmp_dirs.each { |d| directory d }
+
+ desc "Creates tmp directories for cache, sockets, and pids"
+ task create: tmp_dirs
+
+ namespace :cache do
+ # desc "Clears all files and directories in tmp/cache"
+ task :clear do
+ FileUtils.rm_rf(Dir['tmp/cache/[^.]*'])
+ end
+ end
+
+ namespace :sockets do
+ # desc "Clears all files in tmp/sockets"
+ task :clear do
+ FileUtils.rm(Dir['tmp/sockets/[^.]*'])
+ end
+ end
+
+ namespace :pids do
+ # desc "Clears all files in tmp/pids"
+ task :clear do
+ FileUtils.rm(Dir['tmp/pids/[^.]*'])
+ end
+ end
+end
diff --git a/railties/lib/rails/templates/layouts/application.html.erb b/railties/lib/rails/templates/layouts/application.html.erb
new file mode 100644
index 0000000000..5aecaa712a
--- /dev/null
+++ b/railties/lib/rails/templates/layouts/application.html.erb
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <title><%= @page_title %></title>
+ <style>
+ body { background-color: #fff; color: #333; }
+
+ body, p, ol, ul, td {
+ font-family: helvetica, verdana, arial, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+ }
+
+ pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px;
+ white-space: pre-wrap;
+ }
+
+ a { color: #000; }
+ a:visited { color: #666; }
+ a:hover { color: #fff; background-color:#000; }
+
+ h2 { padding-left: 10px; }
+
+ <%= yield :style %>
+ </style>
+</head>
+<body>
+
+<%= yield %>
+
+</body>
+</html>
diff --git a/railties/lib/rails/templates/rails/info/properties.html.erb b/railties/lib/rails/templates/rails/info/properties.html.erb
new file mode 100644
index 0000000000..d47cbab202
--- /dev/null
+++ b/railties/lib/rails/templates/rails/info/properties.html.erb
@@ -0,0 +1 @@
+<%= @info.html_safe %> \ No newline at end of file
diff --git a/railties/lib/rails/templates/rails/info/routes.html.erb b/railties/lib/rails/templates/rails/info/routes.html.erb
new file mode 100644
index 0000000000..2d8a190986
--- /dev/null
+++ b/railties/lib/rails/templates/rails/info/routes.html.erb
@@ -0,0 +1,9 @@
+<h2>
+ Routes
+</h2>
+
+<p>
+ Routes match in priority from top to bottom
+</p>
+
+<%= @routes_inspector.format(ActionDispatch::Routing::HtmlTableFormatter.new(self)) %>
diff --git a/railties/lib/rails/templates/rails/mailers/email.html.erb b/railties/lib/rails/templates/rails/mailers/email.html.erb
new file mode 100644
index 0000000000..fed96fbc85
--- /dev/null
+++ b/railties/lib/rails/templates/rails/mailers/email.html.erb
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<html><head>
+<meta name="viewport" content="width=device-width" />
+<style type="text/css">
+ html, body, iframe {
+ height: 100%;
+ }
+
+ body {
+ margin: 0;
+ }
+
+ header {
+ width: 100%;
+ padding: 10px 0 0 0;
+ margin: 0;
+ background: white;
+ font: 12px "Lucida Grande", sans-serif;
+ border-bottom: 1px solid #dedede;
+ overflow: hidden;
+ }
+
+ dl {
+ margin: 0 0 10px 0;
+ padding: 0;
+ }
+
+ dt {
+ width: 80px;
+ padding: 1px;
+ float: left;
+ clear: left;
+ text-align: right;
+ color: #7f7f7f;
+ }
+
+ dd {
+ margin-left: 90px; /* 80px + 10px */
+ padding: 1px;
+ }
+
+ dd:empty:before {
+ content: "\00a0"; // &nbsp;
+ }
+
+ iframe {
+ border: 0;
+ width: 100%;
+ }
+</style>
+</head>
+
+<body>
+<header>
+ <dl>
+ <% if @email.respond_to?(:smtp_envelope_from) && Array(@email.from) != Array(@email.smtp_envelope_from) %>
+ <dt>SMTP-From:</dt>
+ <dd><%= @email.smtp_envelope_from %></dd>
+ <% end %>
+
+ <% if @email.respond_to?(:smtp_envelope_to) && @email.to != @email.smtp_envelope_to %>
+ <dt>SMTP-To:</dt>
+ <dd><%= @email.smtp_envelope_to %></dd>
+ <% end %>
+
+ <dt>From:</dt>
+ <dd><%= @email.header['from'] %></dd>
+
+ <% if @email.reply_to %>
+ <dt>Reply-To:</dt>
+ <dd><%= @email.header['reply-to'] %></dd>
+ <% end %>
+
+ <dt>To:</dt>
+ <dd><%= @email.header['to'] %></dd>
+
+ <% if @email.cc %>
+ <dt>CC:</dt>
+ <dd><%= @email.header['cc'] %></dd>
+ <% end %>
+
+ <dt>Date:</dt>
+ <dd><%= Time.current.rfc2822 %></dd>
+
+ <dt>Subject:</dt>
+ <dd><strong><%= @email.subject %></strong></dd>
+
+ <% unless @email.attachments.nil? || @email.attachments.empty? %>
+ <dt>Attachments:</dt>
+ <dd>
+ <%= @email.attachments.map { |a| a.respond_to?(:original_filename) ? a.original_filename : a.filename }.join(', ') %>
+ </dd>
+ <% end %>
+
+ <% if @email.multipart? %>
+ <dd>
+ <select onchange="formatChanged(this);">
+ <option <%= request.format == Mime[:html] ? 'selected' : '' %> value="?part=text%2Fhtml">View as HTML email</option>
+ <option <%= request.format == Mime[:text] ? 'selected' : '' %> value="?part=text%2Fplain">View as plain-text email</option>
+ </select>
+ </dd>
+ <% end %>
+ </dl>
+</header>
+
+<% if @part && @part.mime_type %>
+ <iframe seamless name="messageBody" src="?part=<%= Rack::Utils.escape(@part.mime_type) %>"></iframe>
+<% else %>
+ <p>
+ You are trying to preview an email that does not have any content.
+ This is probably because the <em>mail</em> method has not been called in <em><%= @preview.preview_name %>#<%= @email_action %></em>.
+ </p>
+<% end %>
+
+<script>
+ function formatChanged(form) {
+ var part_name = form.options[form.selectedIndex].value
+ var iframe =document.getElementsByName('messageBody')[0];
+ iframe.contentWindow.location.replace(part_name);
+
+ if (history.replaceState) {
+ var url = location.pathname.replace(/\.(txt|html)$/, '');
+ var format = /html/.test(part_name) ? '.html' : '.txt';
+ window.history.replaceState({}, '', url + format);
+ }
+ }
+</script>
+
+</body>
+</html>
diff --git a/railties/lib/rails/templates/rails/mailers/index.html.erb b/railties/lib/rails/templates/rails/mailers/index.html.erb
new file mode 100644
index 0000000000..000930c039
--- /dev/null
+++ b/railties/lib/rails/templates/rails/mailers/index.html.erb
@@ -0,0 +1,8 @@
+<% @previews.each do |preview| %>
+<h3><%= link_to preview.preview_name.titleize, url_for(controller: "rails/mailers", action: "preview", path: preview.preview_name) %></h3>
+<ul>
+<% preview.emails.each do |email| %>
+<li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{preview.preview_name}/#{email}") %></li>
+<% end %>
+</ul>
+<% end %>
diff --git a/railties/lib/rails/templates/rails/mailers/mailer.html.erb b/railties/lib/rails/templates/rails/mailers/mailer.html.erb
new file mode 100644
index 0000000000..c12ead0f90
--- /dev/null
+++ b/railties/lib/rails/templates/rails/mailers/mailer.html.erb
@@ -0,0 +1,6 @@
+<h3><%= @preview.preview_name.titleize %></h3>
+<ul>
+<% @preview.emails.each do |email| %>
+<li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{@preview.preview_name}/#{email}") %></li>
+<% end %>
+</ul>
diff --git a/railties/lib/rails/templates/rails/welcome/index.html.erb b/railties/lib/rails/templates/rails/welcome/index.html.erb
new file mode 100644
index 0000000000..acf04af416
--- /dev/null
+++ b/railties/lib/rails/templates/rails/welcome/index.html.erb
@@ -0,0 +1,268 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Ruby on Rails: Welcome aboard</title>
+ <style media="screen">
+ body {
+ margin: 0;
+ margin-bottom: 25px;
+ padding: 0;
+ background-color: #f0f0f0;
+ font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
+ font-size: 13px;
+ color: #333;
+ }
+
+ h1 {
+ font-size: 28px;
+ color: #000;
+ }
+
+ a {
+ color: #03c;
+ }
+
+ a:hover {
+ background-color: #03c;
+ color: white;
+ text-decoration: none;
+ }
+
+ #page {
+ background-color: #f0f0f0;
+ width: 750px;
+ margin: 0;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ #content {
+ float: left;
+ background-color: white;
+ border: 3px solid #aaa;
+ border-top: none;
+ padding: 25px;
+ width: 500px;
+ }
+
+ #sidebar {
+ float: right;
+ width: 175px;
+ }
+
+ #footer {
+ clear: both;
+ }
+
+ #header, #about, #getting-started {
+ padding-left: 75px;
+ padding-right: 30px;
+ }
+
+ #header {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAABACAYAAABY1SR7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAGZhJREFUeNqsWwmUXGWV/t5Sr9aurl6qO0l3Z9/DEoJh18gZQGAUxPHIyQHH7eioZ8bjnAFHZ0RndNxxRBhGcUbxoKIHBkTEcUYREIHIGpKQjUDS6U660/tSVV3Lq/fefPf/Xy2dBFGYx3npqvde/e/e/97v3u/e/8e4Lt2L8DCCAFcGwF8ZBjYbgM1rAZoO+WLwZhDMu9y4+YcOozbAqzwXNA3GdzX/5hV+KnKO2+GXFj/AvzmW8e72iG202CYiphbY403f9/k3QHZtJ9oWtyCQe7wGX79TKVb7rP9pXJPDVxf0Rz+oyxm4HNWrahFNixdk3EAJbERMWOm4ulctVODNVeEVK0DeRVDgb1wfJgcqUo6duaKnFOH7bm6JmH+5LOEgZprwRIHAV3JYfLjKM55Noz3bBqdcgt0Wg52Kq/cHHkXns0qIukKBlltk9rU2QaiouiefPQ+RdBuseAJeqYTK1CTH8mE4NsyIpRWu8nssCs+xULWpjGVwTvieKl/sV6mIXzOib/OftzuG8d6l8SiVMODyRb46oazg8YPP2Wnvy9ISNqplzsxYAW6hjGhHEmYiBoPC+hRMfFMrESgrBC5n0KS+lq1nPahZh2OXymg9bSNWX/u3FKyKI//7Exx96B4Y8RiCEseq8t0VznyxjMDidFIJ8QSf3hJEOFbZEAHVhIkFTX54fxtnIW5pJUQIeZ8ooZShkInuDOLpFIX1ldtCBix7KI/k4E7OwbTjcNIdiCQzsONp2LEk7GgUnZsuQN9lW2En45xlukrUghWzeZq8FsXsi8+gND6MSCqD9k3nwulIUShKZxt0LYPWortRSY0NXreC8J6pZNDChEDh53PT1NIPLaEnLbQKNTETEaR7sycA0jD1INXZAnzObjTbiWh7Vr1A3Knn4nciu+lCvstUig09cp96cVCtcELoFpEIFUjjyIM/osWIg+IMXS3DcfNwZ3NQHmmKU9OqroX2jWdgatduuPkpmA4ViZrK9RqKABEBtg9tDeW+oUIyTIYuFaX7eCG4aqbU+hhKocD3UBoZISBLiC9cpAQKyq5SQo6OjVswtec5VHLTiHUuIN4WonXlqUj2riS0DIUXwZlERFHSK+SQGzqI3MHdmNm7CzMvvowF527B8qvejZ3/+iXk9vVTao5tiTKN0OUHISZEGS/8W6UbRdoTSHe3E1f+CRaR3xhBLVJSIQ7qleZQGBigZYoYdR+ElUjBaW3H6JMPIrV0Hdo2bEayZ7my0KsdLctPBS64EuWZMYw/9wTGnvod0mtzWH71Vuz66o10bVpK8FIx6orUMejpCKYBTvfM9HXBJtA8z3/1BKDivaksVJmaYsgsYPDnd6LzzAuw8I1XUIGleC1HtDWLnguv5BiX4+jDD2D4sQeV1bQvNXBi6vAb1MGtrEEHjRPgqfZ0qMRJElYYSudfq12nmzAvtJ2yib69iRadRGnySD0Uv5bDtCPou/gqnPY3N6DnLRczgtHxCf4aVnUeUdgw6i6FqM1w292Ujo/TJdB5wHcJ2iDCaBTRmVfw4rkw4yksuvQyJJf0YvrgNiayvBLESS9AYuFqJLLLCPb4SQWulosojhxmeCeoDeaQSoVuy8lPtSKxYKnC2Bmf+DwtvBgv3/qfTI6uEtGuJV7PCBTIq5zNtt5uxBgyvap30pf55TISfX1Y/PatGPrVvcgPvEyAJ1GenaPZLSy//G2IL+qki43CNCMwk620iovy9FGUJgYwm8gwpK/guRJOS5dyD688h+n9z2L28F4Ujx2ia04jEl8Ad3oGVTePaGcnQ3sKLb1rkD3nIqx594dRIh733n6PmmrrvGj671sjVlxczRWAkxZ0r+rTrhfMJ0uEM8xKUYXONR+5nr57BdpP24TCsX6M/f5F5AYLWPauK9F11htUwjOIL8GNZH1qpKwiyVGELk0OoDj2EtziFOaODSN3aC/v24xmZzAU51TgcJKd/DktHo9jyRXvg0Or7PvejTj22KPKiyafew6zg8MYypVLNsLkJ2bxaZXM4i5EmCBPsEaoWJUUpfeSK7DgvEtQmh4ihTDQdf5FOHDHr7HqPVeh99KL4OVzpE50N18CtqnCdBCY6rsEcTsqIGUGD6rY9e3bMPzIHmTWLsbqa7ai84wL6YrTqEyOqEmwonEExSoO//R7dLcJWiWCueF+7P7mjZAUY8YdJZqySMo24j5zQSybQdeyhdrX5imho4NhEEnkRbkDQyjSRVJLeziCgef/6avIrFuOtR95P2lJNSSshg4l6rdm+Ht9inWsqIOX7voN+u/eRoEM5PvHMbbjGcwcfg7jO3YxbCcRiaaYQOXnpEaFGeahGQaMCidJRidt8RghS6Q344XQIowmFq2QXdLNdwsx8zUFqCOQNIECVqdp8pESB53Fvhdux9T2FxBb1AWX4XbjDX/HFzjEmgedB4XYKT5D4T0VTLRCtIiTwOBvfovpvS8T+Bm4MyW6jw13tIIDt/9G/TTWk8HKvzgbmd4+YldYQIdixgHJYkC82Ul6UDnQSbEGdsFGZlEWyUyLyiEyYwajRVAoAXNlEjR+pjUCUmiDQcKOORwwgpFfP4cg5mPzTZ9FoqePdGVWuZRPYQNPcgrd0/dCpqpdy3DIsQ4fxtiTu7Hxkx8iRXkcB+94iM86/K0Jx4opi5aOzGJs14toWeLAdYXWxFQCtJlkA+LUq+bI7QR3mj3YoqVNgGcXd5NWUOiZAk9GH86S4jK25jWBLVREl1uK5Voywz6WXf1WLHjTm0lPigSyxoUpnEqU8c26Wyk/Y24RMjhw/yMoj+cQbWvH0isuwuijL6BwaJwcyq7XUTaBP7N3HOU3ke7HSONJb8RTBGoGKZPFyTE8saTZyCPtrC2coxOoTuY5+x4UTzHNsNjR6d6Qa8JJ5BIV8ksVtKzpwcr3v5dyOrzHKMWXizsZAnK6k1ImPDmAqjOmdr9AwXcodzr4kwfQfuY6VKbzyhpGU96S75WxIqb2DaPnvNWKklQD4WSuzB+sVILjOYjm/VARSWKTBQQzlZCFmErYeubzVJJR14SlQtVQMjO0xrXvoulXkq3OKnxAXqSsoSmNUbOM/BV35RjDDz9JrBXpnnEM3vsYjj38LLyZihI8QNAgQhITOCmTO46i+6w1MPm86RVIiC09/RJUGcECCe2UU0G6QIyUjEC5hGaCNd4RqHKU6VuDylQlI2N8hfXDWibEdyhCKXREuZUVUX8lyhh2+Jl5Q/6akSgT4izGn3wBFu+JwYOKj8qwtsbJaYmJuYEZ5AYmFOWXPCN1jTodzeuqM0WtSI1rzXrV0LSNKRFuZLYQ2EYVPjEQVuQUMsCya65GvL1HWUwJS+FNUcBsUiZUQv7aLGlndr+I8ug4XUMVAJw4U7FmI8SFETTmUaGK2gas1SeeP8znoizIEso9DaUIy2FWkNU5V0VYs/azWXKncuCHqgQq1CHiY831H8TGr34erRvXKdD6LD3b+HnRn12qGgdqlmxHZe2aRcy6NbQScl8y8dSOfWQE1yK9YYmqXYww3xhNObemUI2IWraF2d1HMTeeh83MbkUiylKiiMdy2wjzXBjxWYdRiSkhfDVVKGSstxM9l16JxZe/E2+848c49bPXK9D2vPUyEsBOVZMINmpCW6HgEOuIQjXF6FYuAV2aHsWyrVfj9C9er5SR5Kms0PTf8QoZtIo7WSJW+mmRJLGSpDK2ipzV2bK6X6fxtWOCicYVqyhGXkXn+WeTcfape5ZDsPGM91C5iy8LI0s445bd9FkrAFHICt1N8DE+gdyeQczs34+uzeei68LNLGfdea50st6VbiyYmHq+nxTFRSSRVsD3ii7xyeQbdt/M5h/MERMT4i6GjlAWeUxh6HCN8+LIz+5H5zlUbtHSOnVp4MCa51JaIQ16i0kwP9CP0uExPP+JL2DggfuYN8jTJClYxnH4aNimdpp0r7nDkyx9h5gE0+RqSVTyZXXTsMz5FaJyMJrrGLNopyWUIImj//1LjPzuUZLCC5gzVqMwPIglV7/rxCaihFaCPCDOxDUl1EoylFP4mUlFCgPDStLKWB47PnUjrSSsNqrJsa/zR02ZwGjYRoVkEZh0ZHzbfmTPXE85SWrnKip6GeFE2I1iKVBCzNK9pmiVhS1x+Axx7myRJesvgHvvR3rNKmQ3n/OKPVGND1MVXTqHiFK6qVFiwlXgTVDhkq+ChhnyJCW9GeaoIGQOdV0M9YhYZWbvUXrIJJ+rKL6lJ9CYj5Fai0iKqyPkx0HcUsJYrBbtREIJ2H72GxTI/2CL1zAbLkZ8WIxYgUvsKebq6Zl3rEZvymx6echo1N+au9XcS3oHsxWMPrGTFH+CLhsmbhMNRWrNB4SZVSwyJ5WDFRb3DAAmaXf2rPP+6BpbkmStkBLAWwkHmdNWKfYqFaZRp2GGdo+mhpv6bBkNhepRzERpdASeW1aKSZ5RidpoUsRAvQ+NJCnJHHl+bcZ80vjkij661vo/rWMQSitWskgnNv7LP+MNN38NadYuCPtYCItIFTjMRgfeqClkhkFZ+FXCQmpFuyKXii7xNI93LT9szdrUMsNZnJkuwZX6zlKdaqRXrESiq/e19kBC3NisLt+Gc/7jW0gtZ51Bl1MCmUaoM//aRv0aapnF0l362KIUnI6EyuhCUOuWrIVfAZcRAj5NJWJ0C5epP19y1awJLWhdt/a1t3KcGF8Yxb5bbsLItoeYmxZRkRWq46IrR9StX/tcw4oKsYH+nlrZpmbcZQ7R1tDPBvMbdIwofLpVKIfcJy5nCa5WRhnDFkVOx+s5kr29GPzpfUxsuxg0zlQUxSZudG/CqNOSIJxYCclGCA7fDRDpiCK6gIVfidVmWXrHRh0fmBd+eSYIIEcWdRhdJJsWp+aQT1vI9nYjnl3wuhSJLuhAJJ1WQWDisadUELCi0bD1WlscMpq6lrV1Ft0riC9tVcFD8odfDVS9bod5pNGgC3+XFnxsXA2rsw25/gHMTcwiRxdbvLgPsY7s61IktWSZinw6l8SbupNGvUlphB1yZY3aIhfZtRmz4XS3oMoA5JP6BywdvBIr24ytMdzsWjHaMcnI0nXRG5FkdCrnS6gy6QzccxeMZDsJW+r1KbJ4pbKAVy6huXoyauVUaAUjRK5WjN9cH05PCiZl84VfsXaSVTKf191C6F61qCXjtjAORtvTSPb0sgYoEi/UmEmnMj6JkpXA6z2cTAbxxV26GdEEZB12DVVV63BrIuwYaWpCGZyuJBWSFSxPLTB5PH1+rhDDKlQbuvajNUzE+UVyRTTdQt+zWIrGWIJOozo8hjmashq8PkXsZAoty1Yqi/gVnq6ru+p1pUKFTM3dENJzu421TiqKKq3hhUp45apSyM1VGMH0xOi+liz0yOxUyijs2w2DlRjI+8tHB3XUIP+fGBxA9+LFr1kRgwV769p1fPkEQ+9KRq+dKE9MsGKc1BmxltEC7W6CEdW0aUtocIvw0tcSt5JGu3R4OA+zIxW1uKoUOUZzFxmxRp/ai+iz+xi9CK5EVJGdqBNBlG4xdvBlRq9eTQteawhm0MgPLsSGj92gVqjKk8ew/TOfxPjjz8BKxhvLFGHjWUBuJh0Cu6pqD7WCTGz4BDqKpE30rIlj05rw6sKFxuCXPP9O8MEjxQqOTuQwNjJLa1mItaRRGB3GLHnO6znaNmxC/nA/cocPKNoS61iEZVdfEy5LBHVKUieCLY5eeKIiXp6RapJuNVJFMCamYGnOUFyslBo0Xronai0dIfXmnZIqtKhgNIaj/F3ULSLx4j60dnXXy8s/OZe0dyGW7cLOL34arevXI9rayWgYhZPtoJtNqsTbyPKUgwzamyCw867MtG5NBUF9bSBXLCkeKOzDroUutaZODax52yUk5sfgsyrL897+PXtQHTmK7vWnomPpCkSTf3pI7j7/Qmz/5HWY3r5LNziYeC3WPlYsovOJJ7VKVbuPENcgXEyvuV3IbKXpPlcqqh0acqGe2S1oq1jzqmZ+b0mGDJNaM2bnjrHuPnYUifZOtDMKda9ah1RnZ30F99WO9jM2MzouZw0vLdJIuCsiUInOz0vbiVNa9DSBtITyWo3VAV/XG/KmPEuBKrmard7rNxKiyCoN7EBnpXlLCiYTmfibuEHSSSkLV4uzGNr5NEYP7EZb31J0rd6AzMIevtf+g4oIg+7e8iYM3H03J5muw9n3ZquqfwU3aGDdMBqdztr+lXBbhyg+R2xYTb5jN7YG6SKnyh870r8Ki6Py0CiO3fcTNWaCBU3E8FVDr7ZPRjbcDLHO30N/TmazdLk+JFMxVoZh6errUrcmnDQp5o4MocrI4o3N6dmXhp1hoHkOFV2R5CXtVwm3Qc0aBip8Z6lY0HtRpJ8GYz5pVFgxgkaHiaCuDE1gfOAhFdNbJIKxplCKNJqqyoqi0CT9tp9/IyyPE2SryYyDKD9LVKxKUqXbuFOM+yVDN/Rq+0ia1mLmtYNqK8rhTiSpLLNbLkDLuZvQ0X8QBoG+//5fIMjP4AQ/kJkuM+vW+sS1wkgiVSTi0Fq2XqoLFfFYMMkyHSFL2mOpHQmy+aU4xXHoLk6rrIkYiE1JNpZOJjO1ivduOLSkZeuk6/YBwR54jaVv6chXpmZQmJnEssveQjwVcPCXv1IWt4//sUVB7K4WpGTREqhvJCrO5MhtGLMTKWU5pUSpDKs1glhbB4W3VCSpTM6gOl2GQzxJt+RQUMFcOoENrXG0FEhESSvMmIVIZ6uaHL9QZn6Y067VNJueV4bdmYDdktJ7pAJNKKfG+pG/cz8GH/gfGLIARF4o9fs8RWSrUmZxN7Z+9za0sooTPiRuI22bsUMHsevWW1B+iFnYdOgqFWTPPxWnXPdxtK5eV8fB9IH92Pn1m1hz7MQh00Xm/C34+K23MiOXsPvLX8bgbXej5bz1OPs7tzIhduHgnXfghX/8OplEsr6U4ZtV9G69HMvf8wEkKUfgaUeWbs4zX/8Sxm/+AbzxCRVF1VpFM9hrvS2ZmZbuRUh2LpxPw7t60EWK8vgHPoCZ5w4i1pvBps99Bu2nbJ73XLyzB4kvLcAPt27F2LFR9MTjSKbb1L1h4mIq4iNL14u2ZRFJysazZCNHqA0DZXRcuBGnf+bz6v4JLDqVgk3r247DnMdJDkOzffJtDfoY2b0dg08/gbZlq7BiyyWk+MuQ2bAGU9v2snTtQnxBj3pu9OnfYXr/Hiq1EZ0bz0ZsUS+sFUvgDB+DFfh1v3X9Kg4xknfLRNZ21h2/RYTX29avU0pUSwUcuP07KLw0oBZrA5bGozt2MlA5updgzGuJnYyp6rt7778HP37fX+OJW77ZaKzKoo5eOdfRhMehO3+EbXzu8H/dXW/SOTwj0gZqeoVck+h3xES9LDjpVp3QXeRdqSVLkDllrepy5oeHMPH0brq2qdteRmNJwj7pYKFVlr75YrztKw6ya9aFTzF8Tk+pBZrmXRGRdCsSLMiQbKlfE7PLrjarCcSSA0QZvQQevGKncnrfXpVwZTde3+XvqN9b8d4PYfuNn8O+b9zO56K6oGpOiMYreNfSc7eoE+FO00P334XJx3fQzM685zd8/Hqs/uCHGGEy9QEslaT0Cm9t7rVyYqnGWogEGIl+nqUTmyxwTj62HTs/91ks3XqN2u8VBLKZoVt14pe/42oc/O6dzB2+qnEMNGHEPHHbSfiSqloakGP7D7+Dpz79BfT6cRXu5rHatk51Nh9aEaOJu2mOZIf36uDu6EDi3PVoIQGV5efiwSG1Rjny8COY3P4sI1WM2HKx4bpPYdEFlzA9RMOlhCAsLJssYqGxRbcZI8//9MfIrliDvjPOwqqL/xwD996P6rY9zGHWPNMNPf8UJl9+Cdm169G9YWOdapjB8auShsJMc85YdekVWL7lQgroKHd68qMfRcAEu+lrX1GdSdmBKjQn0aOrU9lso5bK53uSLiyscNu10tAy66FganAQD9zwD6jM5ZBe2IeLbvoGWs5YofZQyfKXxbpejl133omfXfth7P/Fz8NRLbXgb0nGNe26GhGST5MzFmEYll2oCl+sd2IZCcWtTKxd6rokwdYVpyK9fB1z1KnIrD0NDt1WiNGB738X3kxJVapiWVmR5pCurc2iSaIkmNJ0Hr+9+WYkMu0YfHI7Dv9+J+766Eew8vSNiFP4WGsGBanhh6bw1K3fRjSdwfSel5FikTT67At4+t9vgVssojA0Rp6VwOyhfjx9262qABrfw1KaJW15YprXvsVcEG1sT5eCji40fXSURVyAvTd9TSmv6nTVifQx/uwzmHiU7kb3Clu+GC27MsY247p07+SihN0m/Kgc6EXRIjmMgDvCF9mcsXJxDgniZSnN3xFLIcc6Yormd1mhCX2QpWc7SteolNUpNUQkIUvJpDkUrsrfqy1L8ZjaFSTrJKLsCbvz6BqxaBwdBReWbJmF3kTa2NYRVYFGHEYKqqFKFXtzMg6uUhaJyzZyQ/d/FdUm8LwmAuYwO/vhQBU+m+ddmy+NpBKNWpIzF7EdRSxrOygMMl6LruUw2tQXOTy1akNFk/XtU/V70H3g6YyNNk5GtOIp/DYvlKp9LoJLWuIl2fADfJ/X71PQ8Jo2Vzbv620OAFI9jtIqCQ7tnfC/JxhNT4dShds4UKvB66s1ftPnRqOh/l13hDDqWGhxqUgTsIV1Fzg5Y7TEpKsK+B/w+sdqUWuqv1CxUN8K/MqHLMnhj/g/J/4/juDky9VSg0kh/zQj322897Pao/8nwAC+AZicLeuzngAAAABJRU5ErkJggg==);
+ background-repeat: no-repeat;
+ background-position: top left;
+ height: 64px;
+ }
+
+ #header h1,
+ #header h2 {
+ margin: 0;
+ }
+
+ #header h2 {
+ color: #888;
+ font-weight: normal;
+ font-size: 16px;
+ }
+
+ #about h3 {
+ margin: 0;
+ margin-bottom: 10px;
+ font-size: 14px;
+ }
+
+ #about-content {
+ background-color: #ffd;
+ border: 1px solid #fc0;
+ margin-left: -55px;
+ margin-right: -10px;
+ }
+
+ #about-content table {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size: 11px;
+ border-collapse: collapse;
+ }
+
+ #about-content td {
+ padding: 10px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ }
+
+ #about-content td.name {
+ font-weight: bold;
+ vertical-align: top;
+ color: #555;
+ }
+
+ #about-content td.value {
+ color: #000;
+ }
+
+ #about-content ul {
+ padding: 0;
+ list-style-type: none;
+ }
+
+ #about-content.failure {
+ background-color: #fcc;
+ border: 1px solid #f00;
+ }
+
+ #about-content.failure p {
+ margin: 0;
+ padding: 10px;
+ }
+
+ #getting-started {
+ border-top: 1px solid #ccc;
+ margin-top: 25px;
+ padding-top: 15px;
+ }
+
+ #getting-started h1 {
+ margin: 0;
+ font-size: 20px;
+ }
+
+ #getting-started h2 {
+ margin: 0;
+ font-size: 14px;
+ font-weight: normal;
+ color: #333;
+ margin-bottom: 25px;
+ }
+
+ #getting-started ol {
+ margin-left: 0;
+ padding-left: 0;
+ }
+
+ #getting-started li {
+ font-size: 18px;
+ color: #888;
+ margin-bottom: 25px;
+ }
+
+ #getting-started li h2 {
+ margin: 0;
+ font-weight: normal;
+ font-size: 18px;
+ color: #333;
+ }
+
+ #getting-started li p {
+ color: #555;
+ font-size: 13px;
+ }
+
+ #sidebar ul {
+ margin-left: 0;
+ padding-left: 0;
+ }
+
+ #sidebar ul h3 {
+ margin-top: 25px;
+ font-size: 16px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #ccc;
+ }
+
+ #sidebar li {
+ list-style-type: none;
+ }
+
+ #sidebar ul.links li {
+ margin-bottom: 5px;
+ }
+
+ .filename {
+ font-style: italic;
+ }
+ </style>
+ <script>
+ function about() {
+ var info = document.getElementById('about-content'),
+ xhr;
+
+ if (info.innerHTML === '') {
+ xhr = new XMLHttpRequest();
+ xhr.open("GET", "/rails/info/properties", false);
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ xhr.send("");
+ info.innerHTML = xhr.responseText;
+ }
+
+ info.style.display = info.style.display === 'none' ? 'block' : 'none';
+ }
+ </script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="sidebar">
+ <ul id="sidebar-items">
+ <li>
+ <h3>Browse the documentation</h3>
+ <ul class="links">
+ <li><a href="http://guides.rubyonrails.org/">Rails Guides</a></li>
+ <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
+ <li><a href="http://www.ruby-doc.org/core/">Ruby core</a></li>
+ <li><a href="http://www.ruby-doc.org/stdlib/">Ruby standard library</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+
+ <div id="content">
+ <div id="header">
+ <h1>Welcome aboard</h1>
+ <h2>You&rsquo;re riding Ruby on Rails!</h2>
+ </div>
+
+ <div id="about">
+ <h3><a href="/rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
+ <div id="about-content" style="display: none"></div>
+ </div>
+
+ <div id="getting-started">
+ <h1>Getting started</h1>
+ <h2>Here&rsquo;s how to get rolling:</h2>
+
+ <ol>
+ <li>
+ <h2>Use <code>bin/rails generate</code> to create your models and controllers</h2>
+ <p>To see all available options, run it without parameters.</p>
+ </li>
+
+ <li>
+ <h2>Set up a root route to replace this page</h2>
+ <p>You're seeing this page because you're running in development mode and you haven't set a root route yet.</p>
+ <p>Routes are set up in <span class="filename">config/routes.rb</span>.</p>
+ </li>
+
+ <li>
+ <h2>Configure your database</h2>
+ <p>If you're not using SQLite (the default), edit <span class="filename">config/database.yml</span> with your username and password.</p>
+ </li>
+ </ol>
+ </div>
+ </div>
+
+ <div id="footer">&nbsp;</div>
+ </div>
+ </body>
+</html>
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
new file mode 100644
index 0000000000..5cc1b5b219
--- /dev/null
+++ b/railties/lib/rails/test_help.rb
@@ -0,0 +1,42 @@
+# Make double-sure the RAILS_ENV is not set to production,
+# so fixtures aren't loaded into that environment
+abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
+
+require "rails/test_unit/minitest_plugin"
+require 'active_support/test_case'
+require 'action_controller'
+require 'action_controller/test_case'
+require 'action_dispatch/testing/integration'
+require 'rails/generators/test_case'
+
+require 'active_support/testing/autorun'
+
+if defined?(ActiveRecord::Base)
+ ActiveRecord::Migration.maintain_test_schema!
+
+ class ActiveSupport::TestCase
+ include ActiveRecord::TestFixtures
+ self.fixture_path = "#{Rails.root}/test/fixtures/"
+ self.file_fixture_path = self.fixture_path + "files"
+ end
+
+ ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
+
+ def create_fixtures(*fixture_set_names, &block)
+ FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, {}, &block)
+ end
+end
+
+class ActionController::TestCase
+ def before_setup # :nodoc:
+ @routes = Rails.application.routes
+ super
+ end
+end
+
+class ActionDispatch::IntegrationTest
+ def before_setup # :nodoc:
+ @routes = Rails.application.routes
+ super
+ end
+end
diff --git a/railties/lib/rails/test_unit/minitest_plugin.rb b/railties/lib/rails/test_unit/minitest_plugin.rb
new file mode 100644
index 0000000000..d39d2f32bf
--- /dev/null
+++ b/railties/lib/rails/test_unit/minitest_plugin.rb
@@ -0,0 +1,91 @@
+require "active_support/core_ext/module/attribute_accessors"
+require "rails/test_unit/reporter"
+require "rails/test_unit/test_requirer"
+
+module Minitest
+ mattr_accessor(:hide_aggregated_results) { false }
+
+ module AggregatedResultSuppresion
+ def aggregated_results
+ super unless Minitest.hide_aggregated_results
+ end
+ end
+
+ SummaryReporter.prepend AggregatedResultSuppresion
+
+ def self.plugin_rails_options(opts, options)
+ executable = ::Rails::TestUnitReporter.executable
+ opts.separator ""
+ opts.separator "Usage: #{executable} [options] [files or directories]"
+ opts.separator "You can run a single test by appending a line number to a filename:"
+ opts.separator ""
+ opts.separator " #{executable} test/models/user_test.rb:27"
+ opts.separator ""
+ opts.separator "You can run multiple files and directories at the same time:"
+ opts.separator ""
+ opts.separator " #{executable} test/controllers test/integration/login_test.rb"
+ opts.separator ""
+ opts.separator "By default test failures and errors are reported inline during a run."
+ opts.separator ""
+
+ opts.separator "Rails options:"
+ opts.on("-e", "--environment ENV",
+ "Run tests in the ENV environment") do |env|
+ options[:environment] = env.strip
+ end
+
+ opts.on("-b", "--backtrace",
+ "Show the complete backtrace") do
+ options[:full_backtrace] = true
+ end
+
+ opts.on("-d", "--defer-output",
+ "Output test failures and errors after the test run") do
+ options[:output_inline] = false
+ end
+
+ opts.on("-f", "--fail-fast",
+ "Abort test run on first failure") do
+ options[:fail_fast] = true
+ end
+
+ options[:output_inline] = true
+ options[:patterns] = opts.order!
+ end
+
+ # Running several Rake tasks in a single command would trip up the runner,
+ # as the patterns would also contain the other Rake tasks.
+ def self.rake_run(patterns) # :nodoc:
+ @rake_patterns = patterns
+ passed = run
+ exit passed unless passed
+ passed
+ end
+
+ def self.plugin_rails_init(options)
+ self.run_with_rails_extension = true
+
+ ENV["RAILS_ENV"] = options[:environment] || "test"
+
+ unless run_with_autorun
+ patterns = defined?(@rake_patterns) ? @rake_patterns : options[:patterns]
+ ::Rails::TestRequirer.require_files(patterns)
+ end
+
+ unless options[:full_backtrace] || ENV["BACKTRACE"]
+ # Plugin can run without Rails loaded, check before filtering.
+ Minitest.backtrace_filter = ::Rails.backtrace_cleaner if ::Rails.respond_to?(:backtrace_cleaner)
+ end
+
+ # Disable the extra failure output after a run, unless output is deferred.
+ self.hide_aggregated_results = options[:output_inline]
+
+ self.reporter << ::Rails::TestUnitReporter.new(options[:io], options)
+ end
+
+ mattr_accessor(:run_with_autorun) { false }
+ mattr_accessor(:run_with_rails_extension) { false }
+end
+
+Minitest.load_plugins
+Minitest.extensions << 'rails'
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
new file mode 100644
index 0000000000..75180ff978
--- /dev/null
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -0,0 +1,18 @@
+if defined?(Rake.application) && Rake.application.top_level_tasks.grep(/^(default$|test(:|$))/).any?
+ ENV['RAILS_ENV'] ||= 'test'
+end
+
+module Rails
+ class TestUnitRailtie < Rails::Railtie
+ config.app_generators do |c|
+ c.test_framework :test_unit, fixture: true,
+ fixture_replacement: nil
+
+ c.integration_tool :test_unit
+ end
+
+ rake_tasks do
+ load "rails/test_unit/testing.rake"
+ end
+ end
+end
diff --git a/railties/lib/rails/test_unit/reporter.rb b/railties/lib/rails/test_unit/reporter.rb
new file mode 100644
index 0000000000..00ea32d1b8
--- /dev/null
+++ b/railties/lib/rails/test_unit/reporter.rb
@@ -0,0 +1,74 @@
+require "active_support/core_ext/class/attribute"
+require "minitest"
+
+module Rails
+ class TestUnitReporter < Minitest::StatisticsReporter
+ class_attribute :executable
+ self.executable = "bin/rails test"
+
+ def record(result)
+ super
+
+ if output_inline? && result.failure && (!result.skipped? || options[:verbose])
+ io.puts
+ io.puts
+ io.puts result.failures.map(&:message)
+ io.puts
+ io.puts format_rerun_snippet(result)
+ io.puts
+ end
+
+ if fail_fast? && result.failure && !result.error? && !result.skipped?
+ raise Interrupt
+ end
+ end
+
+ def report
+ return if output_inline? || filtered_results.empty?
+ io.puts
+ io.puts "Failed tests:"
+ io.puts
+ io.puts aggregated_results
+ end
+
+ def aggregated_results # :nodoc:
+ filtered_results.map { |result| format_rerun_snippet(result) }.join "\n"
+ end
+
+ def filtered_results
+ if options[:verbose]
+ results
+ else
+ results.reject(&:skipped?)
+ end
+ end
+
+ def relative_path_for(file)
+ file.sub(/^#{app_root}\/?/, '')
+ end
+
+ private
+ def output_inline?
+ options[:output_inline]
+ end
+
+ def fail_fast?
+ options[:fail_fast]
+ end
+
+ def format_rerun_snippet(result)
+ # Try to extract path to assertion from backtrace.
+ if result.location =~ /\[(.*)\]\z/
+ assertion_path = $1
+ else
+ assertion_path = result.method(result.name).source_location.join(':')
+ end
+
+ "#{self.executable} #{relative_path_for(assertion_path)}"
+ end
+
+ def app_root
+ @app_root ||= defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
+ end
+ end
+end
diff --git a/railties/lib/rails/test_unit/test_requirer.rb b/railties/lib/rails/test_unit/test_requirer.rb
new file mode 100644
index 0000000000..83d2c55ffd
--- /dev/null
+++ b/railties/lib/rails/test_unit/test_requirer.rb
@@ -0,0 +1,28 @@
+require 'active_support/core_ext/object/blank'
+require 'rake/file_list'
+
+module Rails
+ class TestRequirer # :nodoc:
+ class << self
+ def require_files(patterns)
+ patterns = expand_patterns(patterns)
+
+ Rake::FileList[patterns.compact.presence || 'test/**/*_test.rb'].to_a.each do |file|
+ require File.expand_path(file)
+ end
+ end
+
+ private
+ def expand_patterns(patterns)
+ patterns.map do |arg|
+ arg = arg.gsub(/:(\d+)?$/, '')
+ if Dir.exist?(arg)
+ "#{arg}/**/*_test.rb"
+ else
+ arg
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
new file mode 100644
index 0000000000..6676c6a079
--- /dev/null
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -0,0 +1,45 @@
+gem 'minitest'
+require 'minitest'
+require 'rails/test_unit/minitest_plugin'
+
+task default: :test
+
+desc "Runs all tests in test folder"
+task :test do
+ $: << "test"
+ Minitest.rake_run(["test"])
+end
+
+namespace :test do
+ task :prepare do
+ # Placeholder task for other Railtie and plugins to enhance.
+ # If used with Active Record, this task runs before the database schema is synchronized.
+ end
+
+ task :run => %w[test]
+
+ desc "Run tests quickly, but also reset db"
+ task :db => %w[db:test:prepare test]
+
+ ["models", "helpers", "controllers", "mailers", "integration", "jobs"].each do |name|
+ task name => "test:prepare" do
+ $: << "test"
+ Minitest.rake_run(["test/#{name}"])
+ end
+ end
+
+ task :generators => "test:prepare" do
+ $: << "test"
+ Minitest.rake_run(["test/lib/generators"])
+ end
+
+ task :units => "test:prepare" do
+ $: << "test"
+ Minitest.rake_run(["test/models", "test/helpers", "test/unit"])
+ end
+
+ task :functionals => "test:prepare" do
+ $: << "test"
+ Minitest.rake_run(["test/controllers", "test/mailers", "test/functional"])
+ end
+end
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
new file mode 100644
index 0000000000..df351c4238
--- /dev/null
+++ b/railties/lib/rails/version.rb
@@ -0,0 +1,8 @@
+require_relative 'gem_version'
+
+module Rails
+ # Returns the version of the currently loaded Rails as a string.
+ def self.version
+ VERSION::STRING
+ end
+end
diff --git a/railties/lib/rails/welcome_controller.rb b/railties/lib/rails/welcome_controller.rb
new file mode 100644
index 0000000000..de9cd18b01
--- /dev/null
+++ b/railties/lib/rails/welcome_controller.rb
@@ -0,0 +1,8 @@
+require 'rails/application_controller'
+
+class Rails::WelcomeController < Rails::ApplicationController # :nodoc:
+ layout false
+
+ def index
+ end
+end
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
new file mode 100644
index 0000000000..a06336f698
--- /dev/null
+++ b/railties/railties.gemspec
@@ -0,0 +1,34 @@
+version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip
+
+Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = 'railties'
+ s.version = version
+ s.summary = 'Tools for creating, working with, and running Rails applications.'
+ s.description = 'Rails internals: application bootup, plugins, generators, and rake tasks.'
+
+ s.required_ruby_version = '>= 2.2.2'
+
+ s.license = 'MIT'
+
+ s.author = 'David Heinemeier Hansson'
+ s.email = 'david@loudthinking.com'
+ s.homepage = 'http://www.rubyonrails.org'
+
+ s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'RDOC_MAIN.rdoc', 'exe/**/*', 'lib/**/{*,.[a-z]*}']
+ s.require_path = 'lib'
+
+ s.bindir = 'exe'
+ s.executables = ['rails']
+
+ s.rdoc_options << '--exclude' << '.'
+
+ s.add_dependency 'activesupport', version
+ s.add_dependency 'actionpack', version
+
+ s.add_dependency 'rake', '>= 0.8.7'
+ s.add_dependency 'thor', '>= 0.18.1', '< 2.0'
+ s.add_dependency 'method_source'
+
+ s.add_development_dependency 'actionview', version
+end
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
new file mode 100644
index 0000000000..794d180e5d
--- /dev/null
+++ b/railties/test/abstract_unit.rb
@@ -0,0 +1,33 @@
+ENV["RAILS_ENV"] ||= "test"
+
+require File.expand_path("../../../load_paths", __FILE__)
+
+require 'stringio'
+require 'active_support/testing/autorun'
+require 'active_support/testing/stream'
+require 'fileutils'
+
+require 'active_support'
+require 'action_controller'
+require 'action_view'
+require 'rails/all'
+
+module TestApp
+ class Application < Rails::Application
+ config.root = File.dirname(__FILE__)
+ secrets.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
+ end
+end
+
+# Skips the current run on Rubinius using Minitest::Assertions#skip
+def rubinius_skip(message = '')
+ skip message if RUBY_ENGINE == 'rbx'
+end
+# Skips the current run on JRuby using Minitest::Assertions#skip
+def jruby_skip(message = '')
+ skip message if defined?(JRUBY_VERSION)
+end
+
+class ActiveSupport::TestCase
+ include ActiveSupport::Testing::Stream
+end
diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb
new file mode 100644
index 0000000000..5946c8fd4c
--- /dev/null
+++ b/railties/test/app_loader_test.rb
@@ -0,0 +1,89 @@
+require 'tmpdir'
+require 'abstract_unit'
+require 'rails/app_loader'
+
+class AppLoaderTest < ActiveSupport::TestCase
+ def loader
+ @loader ||= Class.new do
+ extend Rails::AppLoader
+
+ def self.exec_arguments
+ @exec_arguments
+ end
+
+ def self.exec(*args)
+ @exec_arguments = args
+ end
+ end
+ end
+
+ def write(filename, contents=nil)
+ FileUtils.mkdir_p(File.dirname(filename))
+ File.write(filename, contents)
+ end
+
+ def expects_exec(exe)
+ assert_equal [Rails::AppLoader::RUBY, exe], loader.exec_arguments
+ end
+
+ setup do
+ @tmp = Dir.mktmpdir('railties-rails-loader-test-suite')
+ @cwd = Dir.pwd
+ Dir.chdir(@tmp)
+ end
+
+ ['bin', 'script'].each do |script_dir|
+ exe = "#{script_dir}/rails"
+
+ test "is not in a Rails application if #{exe} is not found in the current or parent directories" do
+ def loader.find_executables; end
+
+ assert !loader.exec_app
+ end
+
+ test "is not in a Rails application if #{exe} exists but is a folder" do
+ FileUtils.mkdir_p(exe)
+
+ assert !loader.exec_app
+ end
+
+ ['APP_PATH', 'ENGINE_PATH'].each do |keyword|
+ test "is in a Rails application if #{exe} exists and contains #{keyword}" do
+ write exe, keyword
+
+ loader.exec_app
+
+ expects_exec exe
+ end
+
+ test "is not in a Rails application if #{exe} exists but doesn't contain #{keyword}" do
+ write exe
+
+ assert !loader.exec_app
+ end
+
+ test "is in a Rails application if parent directory has #{exe} containing #{keyword} and chdirs to the root directory" do
+ write "foo/bar/#{exe}"
+ write "foo/#{exe}", keyword
+
+ Dir.chdir('foo/bar')
+
+ loader.exec_app
+
+ expects_exec exe
+
+ # Compare the realpath in case either of them has symlinks.
+ #
+ # This happens in particular in Mac OS X, where @tmp starts
+ # with "/var", and Dir.pwd with "/private/var", due to a
+ # default system symlink var -> private/var.
+ assert_equal File.realpath("#@tmp/foo"), File.realpath(Dir.pwd)
+ end
+ end
+ end
+
+ teardown do
+ Dir.chdir(@cwd)
+ FileUtils.rm_rf(@tmp)
+ end
+end
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
new file mode 100644
index 0000000000..8b83784ed6
--- /dev/null
+++ b/railties/test/application/asset_debugging_test.rb
@@ -0,0 +1,73 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class AssetDebuggingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ build_app(initializers: true)
+ end
+
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
+ app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'application' %>"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/posts', to: "posts#index"
+ end
+ RUBY
+
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ActionController::Base
+ end
+ RUBY
+
+ ENV["RAILS_ENV"] = "production"
+
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ def get(*)
+ Kernel.silence_warnings { super }
+ end
+
+ test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
+ # config.assets.debug and config.assets.compile are false for production environment
+ ENV["RAILS_ENV"] = "production"
+ output = Dir.chdir(app_path){ `bin/rake assets:precompile --trace 2>&1` }
+ assert $?.success?, output
+
+ # Load app env
+ app "production"
+
+ class ::PostsController < ActionController::Base ; end
+
+ # the debug_assets params isn't used if compile is off
+ get '/posts?debug_assets=true'
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body)
+ assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
+ end
+
+ test "assets are served with sourcemaps when compile is true and debug_assets params is true" do
+ add_to_env_config "production", "config.assets.compile = true"
+
+ # Load app env
+ app "production"
+
+ class ::PostsController < ActionController::Base ; end
+
+ get '/posts?debug_assets=true'
+ assert_match(/<script src="\/assets\/application(\.debug)?-([0-z]+)\.js"><\/script>/, last_response.body)
+ end
+ end
+end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
new file mode 100644
index 0000000000..18882e1855
--- /dev/null
+++ b/railties/test/application/assets_test.rb
@@ -0,0 +1,504 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+require 'active_support/json'
+
+module ApplicationTests
+ class AssetsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app(initializers: true)
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def precompile!(env = nil)
+ with_env env.to_h do
+ quietly do
+ precompile_task = "bin/rake assets:precompile --trace 2>&1"
+ output = Dir.chdir(app_path) { %x[ #{precompile_task} ] }
+ assert $?.success?, output
+ output
+ end
+ end
+ end
+
+ def with_env(env)
+ env.each { |k, v| ENV[k.to_s] = v }
+ yield
+ ensure
+ env.each_key { |k| ENV.delete k.to_s }
+ end
+
+ def clean_assets!
+ quietly do
+ assert Dir.chdir(app_path) { system('bin/rake assets:clobber') }
+ end
+ end
+
+ def assert_file_exists(filename)
+ globbed = Dir[filename]
+ assert globbed.one?, "Found #{globbed.size} files matching #{filename}. All files in the directory: #{Dir.entries(File.dirname(filename)).inspect}"
+ end
+
+ def assert_no_file_exists(filename)
+ assert !File.exist?(filename), "#{filename} does exist"
+ end
+
+ test "assets routes have higher priority" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/javascripts/demo.js.erb", "a = <%= image_path('rails.png').inspect %>;"
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '*path', to: lambda { |env| [200, { "Content-Type" => "text/html" }, ["Not an asset"]] }
+ end
+ RUBY
+
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
+
+ get "/assets/demo.js"
+ assert_equal 'a = "/assets/rails.png";', last_response.body.strip
+ end
+
+ test "assets do not require compressors until it is used" do
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
+ add_to_env_config "production", "config.assets.compile = true"
+ add_to_env_config "production", "config.assets.precompile = []"
+
+ # Load app env
+ app "production"
+
+ assert !defined?(Uglifier)
+ get "/assets/demo.js"
+ assert_match "alert()", last_response.body
+ assert defined?(Uglifier)
+ end
+
+ test "precompile creates the file, gives it the original asset's content and run in production as default" do
+ app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
+ app_file "app/assets/javascripts/application.js", "alert();"
+ app_file "app/assets/javascripts/foo/application.js", "alert();"
+
+ precompile!
+
+ files = Dir["#{app_path}/public/assets/application-*.js"]
+ files << Dir["#{app_path}/public/assets/foo/application-*.js"].first
+ files.each do |file|
+ assert_not_nil file, "Expected application.js asset to be generated, but none found"
+ assert_equal "alert();\n", File.read(file)
+ end
+ end
+
+ def test_precompile_does_not_hit_the_database
+ app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
+ app_file "app/assets/javascripts/application.js", "alert();"
+ app_file "app/assets/javascripts/foo/application.js", "alert();"
+ app_file "app/controllers/users_controller.rb", <<-eoruby
+ class UsersController < ApplicationController; end
+ eoruby
+ app_file "app/models/user.rb", <<-eoruby
+ class User < ActiveRecord::Base; raise 'should not be reached'; end
+ eoruby
+
+ precompile! \
+ RAILS_ENV: 'production',
+ DATABASE_URL: 'postgresql://baduser:badpass@127.0.0.1/dbname'
+
+ files = Dir["#{app_path}/public/assets/application-*.js"]
+ files << Dir["#{app_path}/public/assets/foo/application-*.js"].first
+ files.each do |file|
+ assert_not_nil file, "Expected application.js asset to be generated, but none found"
+ assert_equal "alert();".strip, File.read(file).strip
+ end
+ end
+
+ test "precompile application.js and application.css and all other non JS/CSS files" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ app_file "app/assets/stylesheets/application.css", "body{}"
+
+ app_file "app/assets/javascripts/someapplication.js", "alert();"
+ app_file "app/assets/stylesheets/someapplication.css", "body{}"
+
+ app_file "app/assets/javascripts/something.min.js", "alert();"
+ app_file "app/assets/stylesheets/something.min.css", "body{}"
+
+ app_file "app/assets/javascripts/something.else.js.erb", "alert();"
+ app_file "app/assets/stylesheets/something.else.css.erb", "body{}"
+
+ images_should_compile = ["a.png", "happyface.png", "happy_face.png", "happy.face.png",
+ "happy-face.png", "happy.happy_face.png", "happy_happy.face.png",
+ "happy.happy.face.png", "-happy.png", "-happy.face.png",
+ "_happy.face.png", "_happy.png"]
+
+ images_should_compile.each do |filename|
+ app_file "app/assets/images/#{filename}", "happy"
+ end
+
+ precompile!
+
+ images_should_compile = ["a-*.png", "happyface-*.png", "happy_face-*.png", "happy.face-*.png",
+ "happy-face-*.png", "happy.happy_face-*.png", "happy_happy.face-*.png",
+ "happy.happy.face-*.png", "-happy-*.png", "-happy.face-*.png",
+ "_happy.face-*.png", "_happy-*.png"]
+
+ images_should_compile.each do |filename|
+ assert_file_exists("#{app_path}/public/assets/#{filename}")
+ end
+
+ assert_file_exists("#{app_path}/public/assets/application-*.js")
+ assert_file_exists("#{app_path}/public/assets/application-*.css")
+
+ assert_no_file_exists("#{app_path}/public/assets/someapplication-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/someapplication-*.css")
+
+ assert_no_file_exists("#{app_path}/public/assets/something.min-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.min-*.css")
+
+ assert_no_file_exists("#{app_path}/public/assets/something.else-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.else-*.css")
+ end
+
+ test "precompile something.js for directory containing index file" do
+ add_to_config "config.assets.precompile = [ 'something.js' ]"
+ app_file "app/assets/javascripts/something/index.js.erb", "alert();"
+
+ precompile!
+
+ assert_file_exists("#{app_path}/public/assets/something-*.js")
+ end
+
+ test 'precompile use assets defined in app env config' do
+ add_to_env_config 'production', 'config.assets.precompile = [ "something.js" ]'
+ app_file 'app/assets/javascripts/something.js.erb', 'alert();'
+
+ precompile! RAILS_ENV: 'production'
+
+ assert_file_exists("#{app_path}/public/assets/something-*.js")
+ end
+
+ test 'precompile use assets defined in app config and reassigned in app env config' do
+ add_to_config 'config.assets.precompile = [ "something_manifest.js" ]'
+ add_to_env_config 'production', 'config.assets.precompile += [ "another_manifest.js" ]'
+
+ app_file 'app/assets/config/something_manifest.js', '//= link something.js'
+ app_file 'app/assets/config/another_manifest.js', '//= link another.js'
+
+ app_file 'app/assets/javascripts/something.js.erb', 'alert();'
+ app_file 'app/assets/javascripts/another.js.erb', 'alert();'
+
+ precompile! RAILS_ENV: 'production'
+
+ assert_file_exists("#{app_path}/public/assets/something_manifest-*.js")
+ assert_file_exists("#{app_path}/public/assets/something-*.js")
+ assert_file_exists("#{app_path}/public/assets/another_manifest-*.js")
+ assert_file_exists("#{app_path}/public/assets/another-*.js")
+ end
+
+ test "asset pipeline should use a Sprockets::CachedEnvironment when config.assets.digest is true" do
+ add_to_config "config.action_controller.perform_caching = false"
+ add_to_env_config "production", "config.assets.compile = true"
+
+ # Load app env
+ app "production"
+
+ assert_equal Sprockets::CachedEnvironment, Rails.application.assets.class
+ end
+
+ test "precompile creates a manifest file with all the assets listed" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ app_file "app/assets/javascripts/application.js", "alert();"
+
+ precompile!
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_match(/application-([0-z]+)\.js/, assets["assets"]["application.js"])
+ assert_match(/application-([0-z]+)\.css/, assets["assets"]["application.css"])
+ end
+
+ test "the manifest file should be saved by default in the same assets folder" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ add_to_config "config.assets.prefix = '/x'"
+
+ precompile!
+
+ manifest = Dir["#{app_path}/public/x/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_match(/application-([0-z]+)\.js/, assets["assets"]["application.js"])
+ end
+
+ test "assets do not require any assets group gem when manifest file is present" do
+ app_file "app/assets/javascripts/application.js", "alert();"
+ add_to_env_config "production", "config.public_file_server.enabled = true"
+
+ precompile! RAILS_ENV: 'production'
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ asset_path = assets["assets"]["application.js"]
+
+ # Load app env
+ app "production"
+
+ # Checking if Uglifier is defined we can know if Sprockets was reached or not
+ assert !defined?(Uglifier)
+ get "/assets/#{asset_path}"
+ assert_match "alert()", last_response.body
+ assert !defined?(Uglifier)
+ end
+
+ test "precompile properly refers files referenced with asset_path" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
+
+ precompile!
+
+ file = Dir["#{app_path}/public/assets/application-*.css"].first
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
+ end
+
+ test "precompile shouldn't use the digests present in manifest.json" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
+
+ precompile! RAILS_ENV: 'production'
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ asset_path = assets["assets"]["application.css"]
+
+ app_file "app/assets/images/rails.png", "p { url: change }"
+
+ precompile!
+
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_not_equal asset_path, assets["assets"]["application.css"]
+ end
+
+ test "precompile appends the md5 hash to files referenced with asset_path and run in production with digest true" do
+ app_file "app/assets/images/rails.png", "notactuallyapng"
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
+
+ precompile! RAILS_ENV: 'production'
+
+ file = Dir["#{app_path}/public/assets/application-*.css"].first
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
+ end
+
+ test "precompile should handle utf8 filenames" do
+ filename = "レイルズ.png"
+ app_file "app/assets/images/#{filename}", "not an image really"
+ app_file "app/assets/config/manifest.js", "//= link_tree ../images"
+ add_to_config "config.assets.precompile = %w(manifest.js)"
+
+ precompile!
+
+ manifest = Dir["#{app_path}/public/assets/.sprockets-manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert asset_path = assets["assets"].find { |(k, _)| k && k =~ /.png/ }[1]
+
+ # Load app env
+ app "development"
+
+ get "/assets/#{URI.parser.escape(asset_path)}"
+ assert_match "not an image really", last_response.body
+ assert_file_exists("#{app_path}/public/assets/#{asset_path}")
+ end
+
+ test "assets are cleaned up properly" do
+ app_file "public/assets/application.js", "alert();"
+ app_file "public/assets/application.css", "a { color: green; }"
+ app_file "public/assets/subdir/broken.png", "not really an image file"
+
+ clean_assets!
+
+ files = Dir["#{app_path}/public/assets/**/*", "#{app_path}/tmp/cache/assets/development/*",
+ "#{app_path}/tmp/cache/assets/test/*", "#{app_path}/tmp/cache/assets/production/*"]
+ assert_equal 0, files.length, "Expected no assets, but found #{files.join(', ')}"
+ end
+
+ test "assets routes are not drawn when compilation is disabled" do
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
+ add_to_config "config.assets.compile = false"
+
+ # Load app env
+ app "production"
+
+ get "/assets/demo.js"
+ assert_equal 404, last_response.status
+ end
+
+ test "does not stream session cookies back" do
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/omg', :to => "omg#index"
+ end
+ RUBY
+
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # Load app env
+ app "development"
+
+ class ::OmgController < ActionController::Base
+ def index
+ flash[:cool_story] = true
+ render text: "ok"
+ end
+ end
+
+ get "/omg"
+ assert_equal 'ok', last_response.body
+
+ get "/assets/demo.js"
+ assert_match "alert()", last_response.body
+ assert_equal nil, last_response.headers["Set-Cookie"]
+ end
+
+ test "files in any assets/ directories are not added to Sprockets" do
+ %w[app lib vendor].each do |dir|
+ app_file "#{dir}/assets/#{dir}_test.erb", "testing"
+ end
+
+ app_file "app/assets/javascripts/demo.js", "alert();"
+
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # Load app env
+ app "development"
+
+ get "/assets/demo.js"
+ assert_match "alert();", last_response.body
+ assert_equal 200, last_response.status
+ end
+
+ test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
+ app_with_assets_in_view
+
+ # config.assets.debug and config.assets.compile are false for production environment
+ precompile! RAILS_ENV: 'production'
+
+ # Load app env
+ app "production"
+
+ class ::PostsController < ActionController::Base ; end
+
+ # the debug_assets params isn't used if compile is off
+ get '/posts?debug_assets=true'
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body)
+ assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
+ end
+
+ test "assets can access model information when precompiling" do
+ app_file "app/models/post.rb", "class Post; end"
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
+ app_file "app/assets/javascripts/xmlhr.js.erb", "<%= Post.name %>"
+
+ precompile!
+
+ assert_equal "Post\n;\n", File.read(Dir["#{app_path}/public/assets/application-*.js"].first)
+ end
+
+ test "initialization on the assets group should set assets_dir" do
+ require "#{app_path}/config/application"
+ Rails.application.initialize!(:assets)
+ assert_not_nil Rails.application.config.action_controller.assets_dir
+ end
+
+ test "enhancements to assets:precompile should only run once" do
+ app_file "lib/tasks/enhance.rake", "Rake::Task['assets:precompile'].enhance { puts 'enhancement' }"
+ output = precompile!
+ assert_equal 1, output.scan("enhancement").size
+ end
+
+ test "digested assets are not mistakenly removed" do
+ app_file "app/assets/application.js", "alert();"
+ add_to_config "config.assets.compile = true"
+
+ precompile!
+
+ files = Dir["#{app_path}/public/assets/application-*.js"]
+ assert_equal 1, files.length, "Expected digested application.js asset to be generated, but none found"
+ end
+
+ test "digested assets are removed from configured path" do
+ app_file "public/production_assets/application.js", "alert();"
+ add_to_env_config "production", "config.assets.prefix = 'production_assets'"
+
+ ENV["RAILS_ENV"] = nil
+
+ clean_assets!
+
+ files = Dir["#{app_path}/public/production_assets/application-*.js"]
+ assert_equal 0, files.length, "Expected application.js asset to be removed, but still exists"
+ end
+
+ test "asset urls should use the request's protocol by default" do
+ app_with_assets_in_view
+ add_to_config "config.asset_host = 'example.com'"
+ add_to_env_config "development", "config.assets.digest = false"
+
+ # Load app env
+ app "development"
+
+ class ::PostsController < ActionController::Base; end
+
+ get '/posts', {}, {'HTTPS'=>'off'}
+ assert_match('src="http://example.com/assets/application.debug.js', last_response.body)
+ get '/posts', {}, {'HTTPS'=>'on'}
+ assert_match('src="https://example.com/assets/application.debug.js', last_response.body)
+ end
+
+ test "asset urls should be protocol-relative if no request is in scope" do
+ app_file "app/assets/images/rails.png", "notreallyapng"
+ app_file "app/assets/javascripts/image_loader.js.erb", "var src='<%= image_path('rails.png') %>';"
+ add_to_config "config.assets.precompile = %w{rails.png image_loader.js}"
+ add_to_config "config.asset_host = 'example.com'"
+ add_to_env_config "development", "config.assets.digest = false"
+
+ precompile!
+
+ assert_match "src='//example.com/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/image_loader-*.js"].first)
+ end
+
+ test "asset paths should use RAILS_RELATIVE_URL_ROOT by default" do
+ ENV["RAILS_RELATIVE_URL_ROOT"] = "/sub/uri"
+ app_file "app/assets/images/rails.png", "notreallyapng"
+ app_file "app/assets/javascripts/app.js.erb", "var src='<%= image_path('rails.png') %>';"
+ add_to_config "config.assets.precompile = %w{rails.png app.js}"
+ add_to_env_config "development", "config.assets.digest = false"
+
+ precompile!
+
+ assert_match "src='/sub/uri/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/app-*.js"].first)
+ end
+
+ private
+
+ def app_with_assets_in_view
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
+ app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'application' %>"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/posts', :to => "posts#index"
+ end
+ RUBY
+ end
+ end
+end
diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb
new file mode 100644
index 0000000000..1bdced02e9
--- /dev/null
+++ b/railties/test/application/bin_setup_test.rb
@@ -0,0 +1,54 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class BinSetupTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_bin_setup
+ Dir.chdir(app_path) do
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table(:articles) {}
+ end
+ RUBY
+
+ list_tables = lambda { `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip }
+ File.write("log/my.log", "zomg!")
+
+ assert_equal '[]', list_tables.call
+ assert_equal 5, File.size("log/my.log")
+ assert_not File.exist?("tmp/restart.txt")
+ `bin/setup 2>&1`
+ assert_equal 0, File.size("log/my.log")
+ assert_equal '["articles", "schema_migrations"]', list_tables.call
+ assert File.exist?("tmp/restart.txt")
+ end
+ end
+
+ def test_bin_setup_output
+ Dir.chdir(app_path) do
+ app_file 'db/schema.rb', ""
+
+ output = `bin/setup 2>&1`
+ assert_equal(<<-OUTPUT, output)
+== Installing dependencies ==
+The Gemfile's dependencies are satisfied
+
+== Preparing database ==
+
+== Removing old logs and tempfiles ==
+
+== Restarting application server ==
+ OUTPUT
+ end
+ end
+ end
+end
diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb
new file mode 100644
index 0000000000..28b3b2f2d6
--- /dev/null
+++ b/railties/test/application/configuration/custom_test.rb
@@ -0,0 +1,54 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ module ConfigurationTests
+ class CustomTest < ActiveSupport::TestCase
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ FileUtils.rm_rf(new_app) if File.directory?(new_app)
+ end
+
+ test 'access custom configuration point' do
+ add_to_config <<-RUBY
+ config.x.payment_processing.schedule = :daily
+ config.x.payment_processing.retries = 3
+ config.x.super_debugger = true
+ config.x.hyper_debugger = false
+ config.x.nil_debugger = nil
+ RUBY
+ require_environment
+
+ x = Rails.configuration.x
+ assert_equal :daily, x.payment_processing.schedule
+ assert_equal 3, x.payment_processing.retries
+ assert_equal true, x.super_debugger
+ assert_equal false, x.hyper_debugger
+ assert_equal nil, x.nil_debugger
+ assert_nil x.i_do_not_exist.zomg
+ end
+
+ private
+ def new_app
+ File.expand_path("#{app_path}/../new_app")
+ end
+
+ def copy_app
+ FileUtils.cp_r(app_path, new_app)
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ def require_environment
+ require "#{app_path}/config/environment"
+ end
+ end
+ end
+end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
new file mode 100644
index 0000000000..49f63d5d31
--- /dev/null
+++ b/railties/test/application/configuration_test.rb
@@ -0,0 +1,1437 @@
+require "isolation/abstract_unit"
+require 'rack/test'
+require 'env_helpers'
+
+class ::MyMailInterceptor
+ def self.delivering_email(email); email; end
+end
+
+class ::MyOtherMailInterceptor < ::MyMailInterceptor; end
+
+class ::MyPreviewMailInterceptor
+ def self.previewing_email(email); email; end
+end
+
+class ::MyOtherPreviewMailInterceptor < ::MyPreviewMailInterceptor; end
+
+class ::MyMailObserver
+ def self.delivered_email(email); email; end
+end
+
+class ::MyOtherMailObserver < ::MyMailObserver; end
+
+module ApplicationTests
+ class ConfigurationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+ include EnvHelpers
+
+ def new_app
+ File.expand_path("#{app_path}/../new_app")
+ end
+
+ def copy_app
+ FileUtils.cp_r(app_path, new_app)
+ end
+
+ def app(env = 'development')
+ @app ||= begin
+ ENV['RAILS_ENV'] = env
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
+
+ Rails.application
+ ensure
+ ENV.delete 'RAILS_ENV'
+ end
+ end
+
+ def setup
+ build_app
+ boot_rails
+ supress_default_config
+ end
+
+ def teardown
+ teardown_app
+ FileUtils.rm_rf(new_app) if File.directory?(new_app)
+ end
+
+ def supress_default_config
+ FileUtils.mv("#{app_path}/config/environments", "#{app_path}/config/__environments__")
+ end
+
+ def restore_default_config
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ FileUtils.mv("#{app_path}/config/__environments__", "#{app_path}/config/environments")
+ end
+
+ test "Rails.env does not set the RAILS_ENV environment variable which would leak out into rake tasks" do
+ require "rails"
+
+ switch_env "RAILS_ENV", nil do
+ Rails.env = "development"
+ assert_equal "development", Rails.env
+ assert_nil ENV['RAILS_ENV']
+ end
+ end
+
+ test "lib dir is on LOAD_PATH during config" do
+ app_file 'lib/my_logger.rb', <<-RUBY
+ require "logger"
+ class MyLogger < ::Logger
+ end
+ RUBY
+ add_to_top_of_config <<-RUBY
+ require 'my_logger'
+ config.logger = MyLogger.new STDOUT
+ RUBY
+
+ app 'development'
+
+ assert_equal 'MyLogger', Rails.application.config.logger.class.name
+ end
+
+ test "a renders exception on pending migration" do
+ add_to_config <<-RUBY
+ config.active_record.migration_error = :page_load
+ config.consider_all_requests_local = true
+ config.action_dispatch.show_exceptions = true
+ RUBY
+
+ app_file 'db/migrate/20140708012246_create_user.rb', <<-RUBY
+ class CreateUser < ActiveRecord::Migration
+ def change
+ create_table :users
+ end
+ end
+ RUBY
+
+ app 'development'
+
+ ActiveRecord::Migrator.migrations_paths = ["#{app_path}/db/migrate"]
+
+ begin
+ get "/foo"
+ assert_equal 500, last_response.status
+ assert_match "ActiveRecord::PendingMigrationError", last_response.body
+ ensure
+ ActiveRecord::Migrator.migrations_paths = nil
+ end
+ end
+
+ test "Rails.groups returns available groups" do
+ require "rails"
+
+ Rails.env = "development"
+ assert_equal [:default, "development"], Rails.groups
+ assert_equal [:default, "development", :assets], Rails.groups(assets: [:development])
+ assert_equal [:default, "development", :another, :assets], Rails.groups(:another, assets: %w(development))
+
+ Rails.env = "test"
+ assert_equal [:default, "test"], Rails.groups(assets: [:development])
+
+ ENV["RAILS_GROUPS"] = "javascripts,stylesheets"
+ assert_equal [:default, "test", "javascripts", "stylesheets"], Rails.groups
+ end
+
+ test "Rails.application is nil until app is initialized" do
+ require 'rails'
+ assert_nil Rails.application
+ app 'development'
+ assert_equal AppTemplate::Application.instance, Rails.application
+ end
+
+ test "Rails.application responds to all instance methods" do
+ app 'development'
+ assert_respond_to Rails.application, :routes_reloader
+ assert_equal Rails.application.routes_reloader, AppTemplate::Application.routes_reloader
+ end
+
+ test "Rails::Application responds to paths" do
+ app 'development'
+ assert_respond_to AppTemplate::Application, :paths
+ assert_equal ["#{app_path}/app/views"], AppTemplate::Application.paths["app/views"].expanded
+ end
+
+ test "the application root is set correctly" do
+ app 'development'
+ assert_equal Pathname.new(app_path), Rails.application.root
+ end
+
+ test "the application root can be seen from the application singleton" do
+ app 'development'
+ assert_equal Pathname.new(app_path), AppTemplate::Application.root
+ end
+
+ test "the application root can be set" do
+ copy_app
+ add_to_config <<-RUBY
+ config.root = '#{new_app}'
+ RUBY
+
+ use_frameworks []
+
+ app 'development'
+
+ assert_equal Pathname.new(new_app), Rails.application.root
+ end
+
+ test "the application root is Dir.pwd if there is no config.ru" do
+ File.delete("#{app_path}/config.ru")
+
+ use_frameworks []
+
+ Dir.chdir("#{app_path}") do
+ app 'development'
+ assert_equal Pathname.new("#{app_path}"), Rails.application.root
+ end
+ end
+
+ test "Rails.root should be a Pathname" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ app 'development'
+
+ assert_instance_of Pathname, Rails.root
+ end
+
+ test "Rails.public_path should be a Pathname" do
+ add_to_config <<-RUBY
+ config.paths["public"] = "somewhere"
+ RUBY
+
+ app 'development'
+
+ assert_instance_of Pathname, Rails.public_path
+ end
+
+ test "initialize an eager loaded, cache classes app" do
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app 'development'
+
+ assert_equal :require, ActiveSupport::Dependencies.mechanism
+ end
+
+ test "application is always added to eager_load namespaces" do
+ app 'development'
+ assert_includes Rails.application.config.eager_load_namespaces, AppTemplate::Application
+ end
+
+ test "the application can be eager loaded even when there are no frameworks" do
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ use_frameworks []
+
+ assert_nothing_raised do
+ app 'development'
+ end
+ end
+
+ test "filter_parameters should be able to set via config.filter_parameters" do
+ add_to_config <<-RUBY
+ config.filter_parameters += [ :foo, 'bar', lambda { |key, value|
+ value = value.reverse if key =~ /baz/
+ }]
+ RUBY
+
+ assert_nothing_raised do
+ app 'development'
+ end
+ end
+
+ test "filter_parameters should be able to set via config.filter_parameters in an initializer" do
+ app_file 'config/initializers/filter_parameters_logging.rb', <<-RUBY
+ Rails.application.config.filter_parameters += [ :password, :foo, 'bar' ]
+ RUBY
+
+ app 'development'
+
+ assert_equal [:password, :foo, 'bar'], Rails.application.env_config['action_dispatch.parameter_filter']
+ end
+
+ test "config.to_prepare is forwarded to ActionDispatch" do
+ $prepared = false
+
+ add_to_config <<-RUBY
+ config.to_prepare do
+ $prepared = true
+ end
+ RUBY
+
+ assert !$prepared
+
+ app 'development'
+
+ get "/"
+ assert $prepared
+ end
+
+ def assert_utf8
+ assert_equal Encoding::UTF_8, Encoding.default_external
+ assert_equal Encoding::UTF_8, Encoding.default_internal
+ end
+
+ test "skipping config.encoding still results in 'utf-8' as the default" do
+ app 'development'
+ assert_utf8
+ end
+
+ test "config.encoding sets the default encoding" do
+ add_to_config <<-RUBY
+ config.encoding = "utf-8"
+ RUBY
+
+ app 'development'
+ assert_utf8
+ end
+
+ test "config.paths.public sets Rails.public_path" do
+ add_to_config <<-RUBY
+ config.paths["public"] = "somewhere"
+ RUBY
+
+ app 'development'
+ assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path
+ end
+
+ test "In production mode, config.public_file_server.enabled is off by default" do
+ restore_default_config
+
+ with_rails_env "production" do
+ app 'production'
+ assert_not app.config.public_file_server.enabled
+ end
+ end
+
+ test "In production mode, config.public_file_server.enabled is enabled when RAILS_SERVE_STATIC_FILES is set" do
+ restore_default_config
+
+ with_rails_env "production" do
+ switch_env "RAILS_SERVE_STATIC_FILES", "1" do
+ app 'production'
+ assert app.config.public_file_server.enabled
+ end
+ end
+ end
+
+ test "In production mode, config.public_file_server.enabled is disabled when RAILS_SERVE_STATIC_FILES is blank" do
+ restore_default_config
+
+ with_rails_env "production" do
+ switch_env "RAILS_SERVE_STATIC_FILES", " " do
+ app 'production'
+ assert_not app.config.public_file_server.enabled
+ end
+ end
+ end
+
+ test "config.serve_static_files is deprecated" do
+ make_basic_app do |application|
+ assert_deprecated do
+ application.config.serve_static_files = true
+ end
+
+ assert application.config.public_file_server.enabled
+ end
+ end
+
+ test "config.static_cache_control is deprecated" do
+ make_basic_app do |application|
+ assert_deprecated do
+ application.config.static_cache_control = "public, max-age=60"
+ end
+
+ assert_equal application.config.static_cache_control, "public, max-age=60"
+ end
+ end
+
+ test "Use key_generator when secret_key_base is set" do
+ make_basic_app do |application|
+ application.secrets.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
+ application.config.session_store :disabled
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ cookies.signed[:some_key] = "some_value"
+ render text: cookies[:some_key]
+ end
+ end
+
+ get "/"
+
+ secret = app.key_generator.generate_key('signed cookie')
+ verifier = ActiveSupport::MessageVerifier.new(secret)
+ assert_equal 'some_value', verifier.verify(last_response.body)
+ end
+
+ test "application verifier can be used in the entire application" do
+ make_basic_app do |application|
+ application.secrets.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
+ application.config.session_store :disabled
+ end
+
+ message = app.message_verifier(:sensitive_value).generate("some_value")
+
+ assert_equal 'some_value', Rails.application.message_verifier(:sensitive_value).verify(message)
+
+ secret = app.key_generator.generate_key('sensitive_value')
+ verifier = ActiveSupport::MessageVerifier.new(secret)
+ assert_equal 'some_value', verifier.verify(message)
+ end
+
+ test "application message verifier can be used when the key_generator is ActiveSupport::LegacyKeyGenerator" do
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
+ RUBY
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app 'development'
+
+ assert_equal app.env_config['action_dispatch.key_generator'], Rails.application.key_generator
+ assert_equal app.env_config['action_dispatch.key_generator'].class, ActiveSupport::LegacyKeyGenerator
+ message = app.message_verifier(:sensitive_value).generate("some_value")
+ assert_equal 'some_value', Rails.application.message_verifier(:sensitive_value).verify(message)
+ end
+
+ test "warns when secrets.secret_key_base is blank and config.secret_token is set" do
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
+ RUBY
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app 'development'
+
+ assert_deprecated(/You didn't set `secret_key_base`./) do
+ app.env_config
+ end
+ end
+
+ test "raise when secrets.secret_key_base is not a type of string" do
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base: 123
+ YAML
+
+ app 'development'
+
+ assert_raise(ArgumentError) do
+ app.key_generator
+ end
+ end
+
+ test "prefer secrets.secret_token over config.secret_token" do
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_token = ""
+ RUBY
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_token: 3b7cd727ee24e8444053437c36cc66c3
+ YAML
+
+ app 'development'
+
+ assert_equal '3b7cd727ee24e8444053437c36cc66c3', app.secrets.secret_token
+ end
+
+ test "application verifier can build different verifiers" do
+ make_basic_app do |application|
+ application.secrets.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
+ application.config.session_store :disabled
+ end
+
+ default_verifier = app.message_verifier(:sensitive_value)
+ text_verifier = app.message_verifier(:text)
+
+ message = text_verifier.generate('some_value')
+
+ assert_equal 'some_value', text_verifier.verify(message)
+ assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
+ default_verifier.verify(message)
+ end
+
+ assert_equal default_verifier.object_id, app.message_verifier(:sensitive_value).object_id
+ assert_not_equal default_verifier.object_id, text_verifier.object_id
+ end
+
+ test "secrets.secret_key_base is used when config/secrets.yml is present" do
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
+ YAML
+
+ app 'development'
+ assert_equal '3b7cd727ee24e8444053437c36cc66c3', app.secrets.secret_key_base
+ end
+
+ test "secret_key_base is copied from config to secrets when not set" do
+ remove_file "config/secrets.yml"
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c3"
+ RUBY
+
+ app 'development'
+ assert_equal '3b7cd727ee24e8444053437c36cc66c3', app.secrets.secret_key_base
+ end
+
+ test "config.secret_token over-writes a blank secrets.secret_token" do
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
+ RUBY
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base:
+ secret_token:
+ YAML
+
+ app 'development'
+
+ assert_equal 'b3c631c314c0bbca50c1b2843150fe33', app.secrets.secret_token
+ assert_equal 'b3c631c314c0bbca50c1b2843150fe33', app.config.secret_token
+ end
+
+ test "custom secrets saved in config/secrets.yml are loaded in app secrets" do
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
+ aws_access_key_id: myamazonaccesskeyid
+ aws_secret_access_key: myamazonsecretaccesskey
+ YAML
+
+ app 'development'
+
+ assert_equal 'myamazonaccesskeyid', app.secrets.aws_access_key_id
+ assert_equal 'myamazonsecretaccesskey', app.secrets.aws_secret_access_key
+ end
+
+ test "blank config/secrets.yml does not crash the loading process" do
+ app_file 'config/secrets.yml', <<-YAML
+ YAML
+
+ app 'development'
+
+ assert_nil app.secrets.not_defined
+ end
+
+ test "config.secret_key_base over-writes a blank secrets.secret_key_base" do
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_key_base = "iaminallyoursecretkeybase"
+ RUBY
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app 'development'
+
+ assert_equal "iaminallyoursecretkeybase", app.secrets.secret_key_base
+ end
+
+ test "uses ActiveSupport::LegacyKeyGenerator as app.key_generator when secrets.secret_key_base is blank" do
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33"
+ RUBY
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app 'development'
+
+ assert_equal 'b3c631c314c0bbca50c1b2843150fe33', app.config.secret_token
+ assert_equal nil, app.secrets.secret_key_base
+ assert_equal app.key_generator.class, ActiveSupport::LegacyKeyGenerator
+ end
+
+ test "uses ActiveSupport::LegacyKeyGenerator with config.secret_token as app.key_generator when secrets.secret_key_base is blank" do
+ app_file 'config/initializers/secret_token.rb', <<-RUBY
+ Rails.application.config.secret_token = ""
+ RUBY
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base:
+ YAML
+
+ app 'development'
+
+ assert_equal '', app.config.secret_token
+ assert_equal nil, app.secrets.secret_key_base
+ assert_raise ArgumentError, /\AA secret is required/ do
+ app.key_generator
+ end
+ end
+
+ test "protect from forgery is the default in a new app" do
+ make_basic_app
+
+ class ::OmgController < ActionController::Base
+ def index
+ render inline: "<%= csrf_meta_tags %>"
+ end
+ end
+
+ get "/"
+ assert last_response.body =~ /csrf\-param/
+ end
+
+ test "default form builder specified as a string" do
+ app_file 'config/initializers/form_builder.rb', <<-RUBY
+ class CustomFormBuilder < ActionView::Helpers::FormBuilder
+ def text_field(attribute, *args)
+ label(attribute) + super(attribute, *args)
+ end
+ end
+ Rails.configuration.action_view.default_form_builder = "CustomFormBuilder"
+ 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_for(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(/label/, last_response.body)
+ end
+
+ test "default method for update can be changed" do
+ app_file 'app/models/post.rb', <<-RUBY
+ class Post
+ include ActiveModel::Model
+ def to_key; [1]; end
+ def persisted?; true; end
+ end
+ RUBY
+
+ token = "cf50faa3fe97702ca1ae"
+
+ app_file 'app/controllers/posts_controller.rb', <<-RUBY
+ class PostsController < ApplicationController
+ def show
+ render inline: "<%= begin; form_for(Post.new) {}; rescue => e; e.to_s; end %>"
+ end
+
+ def update
+ render text: "update"
+ end
+
+ private
+
+ def form_authenticity_token; token; end # stub the authenticy token
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ RUBY
+
+ app 'development'
+
+ params = { authenticity_token: token }
+
+ get "/posts/1"
+ assert_match(/patch/, last_response.body)
+
+ patch "/posts/1", params
+ assert_match(/update/, last_response.body)
+
+ patch "/posts/1", params
+ assert_equal 200, last_response.status
+
+ put "/posts/1", params
+ assert_match(/update/, last_response.body)
+
+ put "/posts/1", params
+ assert_equal 200, last_response.status
+ end
+
+ test "request forgery token param can be changed" do
+ make_basic_app do |application|
+ application.config.action_controller.request_forgery_protection_token = '_xsrf_token_here'
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ render inline: "<%= csrf_meta_tags %>"
+ end
+ end
+
+ get "/"
+ assert_match "_xsrf_token_here", last_response.body
+ end
+
+ test "sets ActionDispatch.test_app" do
+ make_basic_app
+ assert_equal Rails.application, ActionDispatch.test_app
+ end
+
+ test "sets ActionDispatch::Response.default_charset" do
+ make_basic_app do |application|
+ application.config.action_dispatch.default_charset = "utf-16"
+ end
+
+ assert_equal "utf-16", ActionDispatch::Response.default_charset
+ end
+
+ test "registers interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.interceptors = MyMailInterceptor
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors")
+ end
+
+ test "registers multiple interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.interceptors = [MyMailInterceptor, "MyOtherMailInterceptor"]
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailInterceptor, ::MyOtherMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors")
+ end
+
+ test "registers preview interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.preview_interceptors = MyPreviewMailInterceptor
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [ActionMailer::InlinePreviewInterceptor, ::MyPreviewMailInterceptor], ActionMailer::Base.preview_interceptors
+ end
+
+ test "registers multiple preview interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.preview_interceptors = [MyPreviewMailInterceptor, "MyOtherPreviewMailInterceptor"]
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [ActionMailer::InlinePreviewInterceptor, MyPreviewMailInterceptor, MyOtherPreviewMailInterceptor], ActionMailer::Base.preview_interceptors
+ end
+
+ test "default preview interceptor can be removed" do
+ app_file 'config/initializers/preview_interceptors.rb', <<-RUBY
+ ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [], ActionMailer::Base.preview_interceptors
+ end
+
+ test "registers observers with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.observers = MyMailObserver
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers")
+ end
+
+ test "registers multiple observers with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.observers = [MyMailObserver, "MyOtherMailObserver"]
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal [::MyMailObserver, ::MyOtherMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers")
+ end
+
+ test "allows setting the queue name for the ActionMailer::DeliveryJob" do
+ add_to_config <<-RUBY
+ config.action_mailer.deliver_later_queue_name = 'test_default'
+ RUBY
+
+ app 'development'
+
+ require "mail"
+ _ = ActionMailer::Base
+
+ assert_equal 'test_default', ActionMailer::Base.send(:class_variable_get, "@@deliver_later_queue_name")
+ end
+
+ test "valid timezone is setup correctly" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "Wellington"
+ RUBY
+
+ app 'development'
+
+ assert_equal "Wellington", Rails.application.config.time_zone
+ end
+
+ test "raises when an invalid timezone is defined in the config" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "That big hill over yonder hill"
+ RUBY
+
+ assert_raise(ArgumentError) do
+ app 'development'
+ end
+ end
+
+ test "valid beginning of week is setup correctly" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.beginning_of_week = :wednesday
+ RUBY
+
+ app 'development'
+
+ assert_equal :wednesday, Rails.application.config.beginning_of_week
+ end
+
+ test "raises when an invalid beginning of week is defined in the config" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.beginning_of_week = :invalid
+ RUBY
+
+ assert_raise(ArgumentError) do
+ app 'development'
+ end
+ end
+
+ test "config.action_view.cache_template_loading with cache_classes default" do
+ add_to_config "config.cache_classes = true"
+
+ app 'development'
+ require 'action_view/base'
+
+ assert_equal true, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading without cache_classes default" do
+ add_to_config "config.cache_classes = false"
+
+ app 'development'
+ require 'action_view/base'
+
+ assert_equal false, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading = false" do
+ add_to_config <<-RUBY
+ config.cache_classes = true
+ config.action_view.cache_template_loading = false
+ RUBY
+
+ app 'development'
+ require 'action_view/base'
+
+ assert_equal false, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading = true" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.action_view.cache_template_loading = true
+ RUBY
+
+ app 'development'
+ require 'action_view/base'
+
+ assert_equal true, ActionView::Resolver.caching?
+ end
+
+ test "config.action_view.cache_template_loading with cache_classes in an environment" do
+ build_app(initializers: true)
+ add_to_env_config "development", "config.cache_classes = false"
+
+ # These requires are to emulate an engine loading Action View before the application
+ require 'action_view'
+ require 'action_view/railtie'
+ require 'action_view/base'
+
+ app 'development'
+
+ assert_equal false, ActionView::Resolver.caching?
+ end
+
+ test "config.action_dispatch.show_exceptions is sent in env" do
+ make_basic_app do |application|
+ application.config.action_dispatch.show_exceptions = true
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ render text: env["action_dispatch.show_exceptions"]
+ end
+ end
+
+ get "/"
+ assert_equal 'true', last_response.body
+ end
+
+ test "config.action_controller.wrap_parameters is set in ActionController::Base" do
+ app_file 'config/initializers/wrap_parameters.rb', <<-RUBY
+ ActionController::Base.wrap_parameters format: [:json]
+ RUBY
+
+ app_file 'app/models/post.rb', <<-RUBY
+ class Post
+ def self.attribute_names
+ %w(title)
+ end
+ end
+ RUBY
+
+ app_file 'app/controllers/application_controller.rb', <<-RUBY
+ class ApplicationController < ActionController::Base
+ protect_from_forgery with: :reset_session # as we are testing API here
+ end
+ RUBY
+
+ app_file 'app/controllers/posts_controller.rb', <<-RUBY
+ class PostsController < ApplicationController
+ def create
+ render text: params[:post].inspect
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ RUBY
+
+ app 'development'
+
+ post "/posts.json", '{ "title": "foo", "name": "bar" }', "CONTENT_TYPE" => "application/json"
+ assert_equal '{"title"=>"foo"}', last_response.body
+ end
+
+ test "config.action_controller.permit_all_parameters = true" do
+ app_file 'app/controllers/posts_controller.rb', <<-RUBY
+ class PostsController < ActionController::Base
+ def create
+ render text: params[:post].permitted? ? "permitted" : "forbidden"
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ config.action_controller.permit_all_parameters = true
+ RUBY
+
+ app 'development'
+
+ post "/posts", {post: {"title" =>"zomg"}}
+ assert_equal 'permitted', last_response.body
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters = :raise" do
+ app_file 'app/controllers/posts_controller.rb', <<-RUBY
+ class PostsController < ActionController::Base
+ def create
+ render text: params.require(:post).permit(:name)
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ config.action_controller.action_on_unpermitted_parameters = :raise
+ RUBY
+
+ app 'development'
+
+ assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
+
+ post "/posts", {post: {"title" =>"zomg"}}
+ assert_match "We're sorry, but something went wrong", last_response.body
+ end
+
+ test "config.action_controller.always_permitted_parameters are: controller, action by default" do
+ app 'development'
+ assert_equal %w(controller action), ActionController::Parameters.always_permitted_parameters
+ end
+
+ test "config.action_controller.always_permitted_parameters = ['controller', 'action', 'format']" do
+ add_to_config <<-RUBY
+ config.action_controller.always_permitted_parameters = %w( controller action format )
+ RUBY
+
+ app 'development'
+
+ assert_equal %w( controller action format ), ActionController::Parameters.always_permitted_parameters
+ end
+
+ test "config.action_controller.always_permitted_parameters = ['controller','action','format'] does not raise exeception" do
+ app_file 'app/controllers/posts_controller.rb', <<-RUBY
+ class PostsController < ActionController::Base
+ def create
+ render text: params.permit(post: [:title])
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ config.action_controller.always_permitted_parameters = %w( controller action format )
+ config.action_controller.action_on_unpermitted_parameters = :raise
+ RUBY
+
+ app 'development'
+
+ assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
+
+ post "/posts", {post: {"title" =>"zomg"}, format: "json"}
+ assert_equal 200, last_response.status
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is :log by default on development" do
+ app 'development'
+
+ assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is :log by default on test" do
+ app 'test'
+
+ assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is false by default on production" do
+ app 'production'
+
+ assert_equal false, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_dispatch.ignore_accept_header" do
+ make_basic_app do |application|
+ application.config.action_dispatch.ignore_accept_header = true
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ respond_to do |format|
+ format.html { render text: "HTML" }
+ format.xml { render text: "XML" }
+ end
+ end
+ end
+
+ get "/", {}, "HTTP_ACCEPT" => "application/xml"
+ assert_equal 'HTML', last_response.body
+
+ get "/", { format: :xml }, "HTTP_ACCEPT" => "application/xml"
+ assert_equal 'XML', last_response.body
+ end
+
+ test "Rails.application#env_config exists and include some existing parameters" do
+ make_basic_app
+
+ assert_respond_to app, :env_config
+ assert_equal app.env_config['action_dispatch.parameter_filter'], app.config.filter_parameters
+ assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions
+ assert_equal app.env_config['action_dispatch.logger'], Rails.logger
+ assert_equal app.env_config['action_dispatch.backtrace_cleaner'], Rails.backtrace_cleaner
+ assert_equal app.env_config['action_dispatch.key_generator'], Rails.application.key_generator
+ end
+
+ test "config.colorize_logging default is true" do
+ make_basic_app
+ assert app.config.colorize_logging
+ end
+
+ test "config.session_store with :active_record_store with activerecord-session_store gem" do
+ begin
+ make_basic_app do |application|
+ ActionDispatch::Session::ActiveRecordStore = Class.new(ActionDispatch::Session::CookieStore)
+ application.config.session_store :active_record_store
+ end
+ ensure
+ ActionDispatch::Session.send :remove_const, :ActiveRecordStore
+ end
+ end
+
+ test "config.session_store with :active_record_store without activerecord-session_store gem" do
+ assert_raise RuntimeError, /activerecord-session_store/ do
+ make_basic_app do |application|
+ application.config.session_store :active_record_store
+ end
+ end
+ end
+
+ test "config.log_level with custom logger" do
+ make_basic_app do |application|
+ application.config.logger = Logger.new(STDOUT)
+ application.config.log_level = :info
+ end
+ assert_equal Logger::INFO, Rails.logger.level
+ end
+
+ test "respond_to? accepts include_private" do
+ make_basic_app
+
+ assert_not Rails.configuration.respond_to?(:method_missing)
+ assert Rails.configuration.respond_to?(:method_missing, true)
+ end
+
+ test "config.active_record.dump_schema_after_migration is false on production" do
+ build_app
+
+ app 'production'
+
+ assert_not ActiveRecord::Base.dump_schema_after_migration
+ end
+
+ test "config.active_record.dump_schema_after_migration is true by default on development" do
+ app 'development'
+
+ assert ActiveRecord::Base.dump_schema_after_migration
+ end
+
+ test "config.annotations wrapping SourceAnnotationExtractor::Annotation class" do
+ make_basic_app do |application|
+ application.config.annotations.register_extensions("coffee") do |tag|
+ /#\s*(#{tag}):?\s*(.*)$/
+ end
+ end
+
+ assert_not_nil SourceAnnotationExtractor::Annotation.extensions[/\.(coffee)$/]
+ end
+
+ test "rake_tasks block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ rake_tasks do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app 'development'
+ assert_not Rails.configuration.ran_block
+
+ require 'rake'
+ require 'rake/testtask'
+ require 'rdoc/task'
+
+ Rails.application.load_tasks
+ assert Rails.configuration.ran_block
+ end
+
+ test "generators block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ generators do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app 'development'
+ assert_not Rails.configuration.ran_block
+
+ Rails.application.load_generators
+ assert Rails.configuration.ran_block
+ end
+
+ test "console block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ console do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app 'development'
+ assert_not Rails.configuration.ran_block
+
+ Rails.application.load_console
+ assert Rails.configuration.ran_block
+ end
+
+ test "runner block works at instance level" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.ran_block = false
+
+ runner do
+ config.ran_block = true
+ end
+ end
+ RUBY
+
+ app 'development'
+ assert_not Rails.configuration.ran_block
+
+ Rails.application.load_runner
+ assert Rails.configuration.ran_block
+ end
+
+ test "loading the first existing database configuration available" do
+ app_file 'config/environments/development.rb', <<-RUBY
+
+ Rails.application.configure do
+ config.paths.add 'config/database', with: 'config/nonexistent.yml'
+ config.paths['config/database'] << 'config/database.yml'
+ end
+ RUBY
+
+ app 'development'
+
+ assert_kind_of Hash, Rails.application.config.database_configuration
+ end
+
+ test 'raises with proper error message if no database configuration found' do
+ FileUtils.rm("#{app_path}/config/database.yml")
+ app 'development'
+ err = assert_raises RuntimeError do
+ Rails.application.config.database_configuration
+ end
+ assert_match 'config/database', err.message
+ end
+
+ test 'config.action_mailer.show_previews defaults to true in development' do
+ app 'development'
+
+ assert Rails.application.config.action_mailer.show_previews
+ end
+
+ test 'config.action_mailer.show_previews defaults to false in production' do
+ app 'production'
+
+ assert_equal false, Rails.application.config.action_mailer.show_previews
+ end
+
+ test 'config.action_mailer.show_previews can be set in the configuration file' do
+ add_to_config <<-RUBY
+ config.action_mailer.show_previews = true
+ RUBY
+
+ app 'production'
+
+ assert_equal true, Rails.application.config.action_mailer.show_previews
+ end
+
+ test "config_for loads custom configuration from yaml files" do
+ app_file 'config/custom.yml', <<-RUBY
+ development:
+ key: 'custom key'
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app 'development'
+
+ assert_equal 'custom key', Rails.application.config.my_custom_config['key']
+ end
+
+ test "config_for raises an exception if the file does not exist" do
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ exception = assert_raises(RuntimeError) do
+ app 'development'
+ end
+
+ assert_equal "Could not load configuration. No such file - #{app_path}/config/custom.yml", exception.message
+ end
+
+ test "config_for without the environment configured returns an empty hash" do
+ app_file 'config/custom.yml', <<-RUBY
+ test:
+ key: 'custom key'
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app 'development'
+
+ assert_equal({}, Rails.application.config.my_custom_config)
+ end
+
+ test "config_for with empty file returns an empty hash" do
+ app_file 'config/custom.yml', <<-RUBY
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app 'development'
+
+ assert_equal({}, Rails.application.config.my_custom_config)
+ end
+
+ test "config_for containing ERB tags should evaluate" do
+ app_file 'config/custom.yml', <<-RUBY
+ development:
+ key: <%= 'custom key' %>
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ app 'development'
+
+ assert_equal 'custom key', Rails.application.config.my_custom_config['key']
+ end
+
+ test "config_for with syntax error show a more descriptive exception" do
+ app_file 'config/custom.yml', <<-RUBY
+ development:
+ key: foo:
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom')
+ RUBY
+
+ exception = assert_raises(RuntimeError) do
+ app 'development'
+ end
+
+ assert_match 'YAML syntax error occurred while parsing', exception.message
+ end
+
+ test "config_for allows overriding the environment" do
+ app_file 'config/custom.yml', <<-RUBY
+ test:
+ key: 'walrus'
+ production:
+ key: 'unicorn'
+ RUBY
+
+ add_to_config <<-RUBY
+ config.my_custom_config = config_for('custom', env: 'production')
+ RUBY
+ require "#{app_path}/config/environment"
+
+ assert_equal 'unicorn', Rails.application.config.my_custom_config['key']
+ end
+
+ test "api_only is false by default" do
+ app 'development'
+ refute Rails.application.config.api_only
+ end
+
+ test "api_only generator config is set when api_only is set" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+ app 'development'
+
+ Rails.application.load_generators
+ assert Rails.configuration.api_only
+ end
+
+ test "debug_exception_response_format is :api by default if only_api is enabled" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+ app 'development'
+
+ assert_equal :api, Rails.configuration.debug_exception_response_format
+ end
+
+ test "debug_exception_response_format can be override" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ app_file 'config/environments/development.rb', <<-RUBY
+ Rails.application.configure do
+ config.debug_exception_response_format = :default
+ end
+ RUBY
+
+ app 'development'
+
+ assert_equal :default, Rails.configuration.debug_exception_response_format
+ end
+ end
+end
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
new file mode 100644
index 0000000000..7bf123d12b
--- /dev/null
+++ b/railties/test/application/console_test.rb
@@ -0,0 +1,165 @@
+require 'isolation/abstract_unit'
+
+class ConsoleTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def load_environment(sandbox = false)
+ require "#{rails_root}/config/environment"
+ Rails.application.sandbox = sandbox
+ Rails.application.load_console
+ end
+
+ def irb_context
+ Object.new.extend(Rails::ConsoleMethods)
+ end
+
+ def test_app_method_should_return_integration_session
+ TestHelpers::Rack.send :remove_method, :app
+ load_environment
+ console_session = irb_context.app
+ assert_instance_of ActionDispatch::Integration::Session, console_session
+ end
+
+ def test_app_can_access_path_helper_method
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ load_environment
+ console_session = irb_context.app
+ assert_equal '/foo', console_session.foo_path
+ end
+
+ def test_new_session_should_return_integration_session
+ load_environment
+ session = irb_context.new_session
+ assert_instance_of ActionDispatch::Integration::Session, session
+ end
+
+ def test_reload_should_fire_preparation_and_cleanup_callbacks
+ load_environment
+ a = b = c = nil
+
+ # TODO: These should be defined on the initializer
+ ActionDispatch::Reloader.to_cleanup { a = b = c = 1 }
+ ActionDispatch::Reloader.to_cleanup { b = c = 2 }
+ ActionDispatch::Reloader.to_prepare { c = 3 }
+
+ # Hide Reloading... output
+ silence_stream(STDOUT) { irb_context.reload! }
+
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+ end
+
+ def test_reload_should_reload_constants
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ attr_accessor :name
+ end
+ MODEL
+
+ load_environment
+ assert User.new.respond_to?(:name)
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ attr_accessor :name, :age
+ end
+ MODEL
+
+ assert !User.new.respond_to?(:age)
+ silence_stream(STDOUT) { irb_context.reload! }
+ assert User.new.respond_to?(:age)
+ end
+
+ def test_access_to_helpers
+ load_environment
+ helper = irb_context.helper
+ assert_not_nil helper
+ assert_instance_of ActionView::Base, helper
+ assert_equal 'Once upon a time in a world...',
+ helper.truncate('Once upon a time in a world far far away')
+ end
+end
+
+begin
+ require "pty"
+rescue LoadError
+end
+
+class FullStackConsoleTest < ActiveSupport::TestCase
+ def setup
+ skip "PTY unavailable" unless defined?(PTY) && PTY.respond_to?(:open)
+
+ build_app
+ app_file 'app/models/post.rb', <<-CODE
+ class Post < ActiveRecord::Base
+ end
+ CODE
+ system "#{app_path}/bin/rails runner 'Post.connection.create_table :posts'"
+
+ @master, @slave = PTY.open
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def assert_output(expected, timeout = 1)
+ timeout = Time.now + timeout
+
+ output = ""
+ until output.include?(expected) || Time.now > timeout
+ if IO.select([@master], [], [], 0.1)
+ output << @master.read(1)
+ end
+ end
+
+ assert output.include?(expected), "#{expected.inspect} expected, but got:\n\n#{output}"
+ end
+
+ def write_prompt(command, expected_output = nil)
+ @master.puts command
+ assert_output command
+ assert_output expected_output if expected_output
+ assert_output "> "
+ end
+
+ def spawn_console
+ Process.spawn(
+ "#{app_path}/bin/rails console --sandbox",
+ in: @slave, out: @slave, err: @slave
+ )
+
+ assert_output "> ", 30
+ end
+
+ def test_sandbox
+ spawn_console
+
+ write_prompt "Post.count", "=> 0"
+ write_prompt "Post.create"
+ write_prompt "Post.count", "=> 1"
+ @master.puts "quit"
+
+ spawn_console
+
+ write_prompt "Post.count", "=> 0"
+ write_prompt "Post.transaction { Post.create; raise }"
+ write_prompt "Post.count", "=> 0"
+ @master.puts "quit"
+ end
+end
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
new file mode 100644
index 0000000000..84cc6e120b
--- /dev/null
+++ b/railties/test/application/generators_test.rb
@@ -0,0 +1,164 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class GeneratorsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app_const
+ @app_const ||= Class.new(Rails::Application)
+ end
+
+ def with_config
+ require "rails/all"
+ require "rails/generators"
+ yield app_const.config
+ end
+
+ def with_bare_config
+ require "rails"
+ require "rails/generators"
+ yield app_const.config
+ end
+
+ test "allow running plugin new generator inside Rails app directory" do
+ FileUtils.cd(rails_root){ `ruby bin/rails plugin new vendor/plugins/bukkits` }
+ assert File.exist?(File.join(rails_root, "vendor/plugins/bukkits/test/dummy/config/application.rb"))
+ end
+
+ test "generators default values" do
+ with_bare_config do |c|
+ assert_equal(true, c.generators.colorize_logging)
+ assert_equal({}, c.generators.aliases)
+ assert_equal({}, c.generators.options)
+ assert_equal({}, c.generators.fallbacks)
+ end
+ end
+
+ test "generators set rails options" do
+ with_bare_config do |c|
+ c.generators.orm = :data_mapper
+ c.generators.test_framework = :rspec
+ c.generators.helper = false
+ expected = { rails: { orm: :data_mapper, test_framework: :rspec, helper: false } }
+ assert_equal(expected, c.generators.options)
+ end
+ end
+
+ test "generators set rails aliases" do
+ with_config do |c|
+ c.generators.aliases = { rails: { test_framework: "-w" } }
+ expected = { rails: { test_framework: "-w" } }
+ assert_equal expected, c.generators.aliases
+ end
+ end
+
+ test "generators aliases, options, templates and fallbacks on initialization" do
+ add_to_config <<-RUBY
+ config.generators.rails aliases: { test_framework: "-w" }
+ config.generators.orm :data_mapper
+ config.generators.test_framework :rspec
+ config.generators.fallbacks[:shoulda] = :test_unit
+ config.generators.templates << "some/where"
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert_equal :rspec, Rails::Generators.options[:rails][:test_framework]
+ assert_equal "-w", Rails::Generators.aliases[:rails][:test_framework]
+ assert_equal Hash[shoulda: :test_unit], Rails::Generators.fallbacks
+ assert_equal ["some/where"], Rails::Generators.templates_path
+ end
+
+ test "generators no color on initialization" do
+ add_to_config <<-RUBY
+ config.generators.colorize_logging = false
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert_equal Thor::Base.shell, Thor::Shell::Basic
+ end
+
+ test "generators with hashes for options and aliases" do
+ with_bare_config do |c|
+ c.generators do |g|
+ g.orm :data_mapper, migration: false
+ g.plugin aliases: { generator: "-g" },
+ generator: true
+ end
+
+ expected = {
+ rails: { orm: :data_mapper },
+ plugin: { generator: true },
+ data_mapper: { migration: false }
+ }
+
+ assert_equal expected, c.generators.options
+ assert_equal({ plugin: { generator: "-g" } }, c.generators.aliases)
+ end
+ end
+
+ test "generators with string and hash for options should generate symbol keys" do
+ with_bare_config do |c|
+ c.generators do |g|
+ g.orm 'data_mapper', migration: false
+ end
+
+ expected = {
+ rails: { orm: :data_mapper },
+ data_mapper: { migration: false }
+ }
+
+ assert_equal expected, c.generators.options
+ end
+ end
+
+ test "api only generators hide assets, helper, js and css namespaces and set api option" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert Rails::Generators.hidden_namespaces.include?("assets")
+ assert Rails::Generators.hidden_namespaces.include?("helper")
+ assert Rails::Generators.hidden_namespaces.include?("js")
+ assert Rails::Generators.hidden_namespaces.include?("css")
+ assert Rails::Generators.options[:rails][:api]
+ assert_equal false, Rails::Generators.options[:rails][:assets]
+ assert_equal false, Rails::Generators.options[:rails][:helper]
+ assert_nil Rails::Generators.options[:rails][:template_engine]
+ end
+
+ test "api only generators allow overriding generator options" do
+ add_to_config <<-RUBY
+ config.generators.helper = true
+ config.api_only = true
+ config.generators.template_engine = :my_template
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert Rails::Generators.options[:rails][:api]
+ assert Rails::Generators.options[:rails][:helper]
+ assert_equal :my_template, Rails::Generators.options[:rails][:template_engine]
+ end
+ end
+end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
new file mode 100644
index 0000000000..13f3250f5b
--- /dev/null
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -0,0 +1,269 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class FrameworksTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # AC & AM
+ test "set load paths set only if action controller or action mailer are in use" do
+ assert_nothing_raised NameError do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ use_frameworks []
+ require "#{app_path}/config/environment"
+ end
+ end
+
+ test "sets action_controller and action_mailer load paths" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ expanded_path = File.expand_path("app/views", app_path)
+ assert_equal expanded_path, ActionController::Base.view_paths[0].to_s
+ assert_equal expanded_path, ActionMailer::Base.view_paths[0].to_s
+ end
+
+ test "allows me to configure default url options for ActionMailer" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.action_mailer.default_url_options = { :host => "test.rails" }
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal "test.rails", ActionMailer::Base.default_url_options[:host]
+ end
+
+ test "Default to HTTPS for ActionMailer URLs when force_ssl is on" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.force_ssl = true
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal "https", ActionMailer::Base.default_url_options[:protocol]
+ end
+
+ test "includes url helpers as action methods" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/foo", :to => lambda { |env| [200, {}, []] }, :as => :foo
+ end
+ RUBY
+
+ app_file "app/mailers/foo.rb", <<-RUBY
+ class Foo < ActionMailer::Base
+ def notify
+ end
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Foo.method_defined?(:foo_url)
+ assert Foo.method_defined?(:main_app)
+ end
+
+ test "allows to not load all helpers for controllers" do
+ add_to_config "config.action_controller.include_all_helpers = false"
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::Base
+ end
+ RUBY
+
+ app_file "app/controllers/foo_controller.rb", <<-RUBY
+ class FooController < ApplicationController
+ def included_helpers
+ render :inline => "<%= from_app_helper -%> <%= from_foo_helper %>"
+ end
+
+ def not_included_helper
+ render :inline => "<%= respond_to?(:from_bar_helper) -%>"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/application_helper.rb", <<-RUBY
+ module ApplicationHelper
+ def from_app_helper
+ "from_app_helper"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/foo_helper.rb", <<-RUBY
+ module FooHelper
+ def from_foo_helper
+ "from_foo_helper"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/bar_helper.rb", <<-RUBY
+ module BarHelper
+ def from_bar_helper
+ "from_bar_helper"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/foo/included_helpers"
+ assert_equal "from_app_helper from_foo_helper", last_response.body
+
+ get "/foo/not_included_helper"
+ assert_equal "false", last_response.body
+ end
+
+ test "action_controller api executes using all the middleware stack" do
+ add_to_config "config.api_only = true"
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ end
+ RUBY
+
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ class OmgController < ApplicationController
+ def show
+ render json: { omg: 'omg' }
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get 'omg/show'
+ assert_equal '{"omg":"omg"}', last_response.body
+ end
+
+ # AD
+ test "action_dispatch extensions are applied to ActionDispatch" do
+ add_to_config "config.action_dispatch.tld_length = 2"
+ require "#{app_path}/config/environment"
+ assert_equal 2, ActionDispatch::Http::URL.tld_length
+ end
+
+ test "assignment config.encoding to default_charset" do
+ charset = 'Shift_JIS'
+ add_to_config "config.encoding = '#{charset}'"
+ require "#{app_path}/config/environment"
+ assert_equal charset, ActionDispatch::Response.default_charset
+ end
+
+ # AS
+ test "if there's no config.active_support.bare, all of ActiveSupport is required" do
+ use_frameworks []
+ require "#{app_path}/config/environment"
+ assert_nothing_raised { [1,2,3].sample }
+ end
+
+ test "config.active_support.bare does not require all of ActiveSupport" do
+ add_to_config "config.active_support.bare = true"
+
+ use_frameworks []
+
+ Dir.chdir("#{app_path}/app") do
+ require "#{app_path}/config/environment"
+ assert_raises(NoMethodError) { "hello".exclude? "lo" }
+ end
+ end
+
+ # AR
+ test "active_record extensions are applied to ActiveRecord" do
+ add_to_config "config.active_record.table_name_prefix = 'tbl_'"
+ require "#{app_path}/config/environment"
+ assert_equal 'tbl_', ActiveRecord::Base.table_name_prefix
+ end
+
+ test "database middleware doesn't initialize when activerecord is not in frameworks" do
+ use_frameworks []
+ require "#{app_path}/config/environment"
+ assert_nil defined?(ActiveRecord::Base)
+ end
+
+ test "use schema cache dump" do
+ Dir.chdir(app_path) do
+ `rails generate model post title:string;
+ bin/rake db:migrate db:schema:cache:dump`
+ end
+ require "#{app_path}/config/environment"
+ ActiveRecord::Base.connection.drop_table("posts") # force drop posts table for test.
+ assert ActiveRecord::Base.connection.schema_cache.tables("posts")
+ end
+
+ test "expire schema cache dump" do
+ Dir.chdir(app_path) do
+ `rails generate model post title:string;
+ bin/rake db:migrate db:schema:cache:dump db:rollback`
+ end
+ silence_warnings {
+ require "#{app_path}/config/environment"
+ assert !ActiveRecord::Base.connection.schema_cache.tables("posts")
+ }
+ end
+
+ test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do
+ begin
+ require "#{app_path}/config/environment"
+ orig_database_url = ENV.delete("DATABASE_URL")
+ orig_rails_env, Rails.env = Rails.env, 'development'
+ ActiveRecord::Base.establish_connection
+ assert ActiveRecord::Base.connection
+ assert_match(/#{ActiveRecord::Base.configurations[Rails.env]['database']}/, ActiveRecord::Base.connection_config[:database])
+ ensure
+ ActiveRecord::Base.remove_connection
+ ENV["DATABASE_URL"] = orig_database_url if orig_database_url
+ Rails.env = orig_rails_env if orig_rails_env
+ end
+ end
+
+ test "active record establish_connection uses DATABASE_URL even if Rails.env is set" do
+ begin
+ require "#{app_path}/config/environment"
+ orig_database_url = ENV.delete("DATABASE_URL")
+ orig_rails_env, Rails.env = Rails.env, 'development'
+ database_url_db_name = "db/database_url_db.sqlite3"
+ ENV["DATABASE_URL"] = "sqlite3:#{database_url_db_name}"
+ ActiveRecord::Base.establish_connection
+ assert ActiveRecord::Base.connection
+ assert_match(/#{database_url_db_name}/, ActiveRecord::Base.connection_config[:database])
+ ensure
+ ActiveRecord::Base.remove_connection
+ ENV["DATABASE_URL"] = orig_database_url if orig_database_url
+ Rails.env = orig_rails_env if orig_rails_env
+ end
+ end
+ end
+end
diff --git a/railties/test/application/initializers/hooks_test.rb b/railties/test/application/initializers/hooks_test.rb
new file mode 100644
index 0000000000..b2cea0a8e1
--- /dev/null
+++ b/railties/test/application/initializers/hooks_test.rb
@@ -0,0 +1,90 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class HooksTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "load initializers" do
+ app_file "config/initializers/foo.rb", "$foo = true"
+ require "#{app_path}/config/environment"
+ assert $foo
+ end
+
+ test "hooks block works correctly without eager_load (before_eager_load is not called)" do
+ add_to_config <<-RUBY
+ $initialization_callbacks = []
+ config.root = "#{app_path}"
+ config.eager_load = false
+ config.before_configuration { $initialization_callbacks << 1 }
+ config.before_initialize { $initialization_callbacks << 2 }
+ config.before_eager_load { Boom }
+ config.after_initialize { $initialization_callbacks << 3 }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal [1,2,3], $initialization_callbacks
+ end
+
+ test "hooks block works correctly with eager_load" do
+ add_to_config <<-RUBY
+ $initialization_callbacks = []
+ config.root = "#{app_path}"
+ config.eager_load = true
+ config.before_configuration { $initialization_callbacks << 1 }
+ config.before_initialize { $initialization_callbacks << 2 }
+ config.before_eager_load { $initialization_callbacks << 3 }
+ config.after_initialize { $initialization_callbacks << 4 }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal [1,2,3,4], $initialization_callbacks
+ end
+
+ test "after_initialize runs after frameworks have been initialized" do
+ $activerecord_configurations = nil
+ add_to_config <<-RUBY
+ config.after_initialize { $activerecord_configurations = ActiveRecord::Base.configurations }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert $activerecord_configurations
+ assert $activerecord_configurations['development']
+ end
+
+ test "after_initialize happens after to_prepare in development" do
+ $order = []
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.after_initialize { $order << :after_initialize }
+ config.to_prepare { $order << :to_prepare }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal [:to_prepare, :after_initialize], $order
+ end
+
+ test "after_initialize happens after to_prepare in production" do
+ $order = []
+ add_to_config <<-RUBY
+ config.cache_classes = true
+ config.after_initialize { $order << :after_initialize }
+ config.to_prepare { $order << :to_prepare }
+ RUBY
+
+ require "#{app_path}/config/application"
+ Rails.env.replace "production"
+ require "#{app_path}/config/environment"
+ assert_equal [:to_prepare, :after_initialize], $order
+ end
+ end
+end
diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb
new file mode 100644
index 0000000000..ab7f29b0f2
--- /dev/null
+++ b/railties/test/application/initializers/i18n_test.rb
@@ -0,0 +1,289 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class I18nTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ require "rails/all"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def load_app
+ require "#{app_path}/config/environment"
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ def assert_fallbacks(fallbacks)
+ fallbacks.each do |locale, expected|
+ actual = I18n.fallbacks[locale]
+ assert_equal expected, actual, "expected fallbacks for #{locale.inspect} to be #{expected.inspect}, but were #{actual.inspect}"
+ end
+ end
+
+ def assert_no_fallbacks
+ assert !I18n.backend.class.included_modules.include?(I18n::Backend::Fallbacks)
+ end
+
+ # Locales
+ test "setting another default locale" do
+ add_to_config <<-RUBY
+ config.i18n.default_locale = :de
+ RUBY
+
+ load_app
+ assert_equal :de, I18n.default_locale
+ end
+
+ # Load paths
+ test "no config locales directory present should return empty load path" do
+ FileUtils.rm_rf "#{app_path}/config/locales"
+ load_app
+ assert_equal [], Rails.application.config.i18n.load_path
+ end
+
+ test "locale files should be added to the load path" do
+ app_file "config/another_locale.yml", "en:\nfoo: ~"
+
+ add_to_config <<-RUBY
+ config.i18n.load_path << config.root.join("config/another_locale.yml").to_s
+ RUBY
+
+ load_app
+ assert_equal [
+ "#{app_path}/config/locales/en.yml", "#{app_path}/config/another_locale.yml"
+ ], Rails.application.config.i18n.load_path
+
+ assert I18n.load_path.include?("#{app_path}/config/locales/en.yml")
+ assert I18n.load_path.include?("#{app_path}/config/another_locale.yml")
+ end
+
+ test "load_path is populated before eager loaded models" do
+ add_to_config <<-RUBY
+ config.cache_classes = true
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file 'app/models/foo.rb', <<-RUBY
+ class Foo < ActiveRecord::Base
+ @foo = I18n.t(:foo)
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [Foo.instance_variable_get('@foo')]] }
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+ assert_equal "1", last_response.body
+ end
+
+ test "locales are reloaded if they change between requests" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [I18n.t(:foo)]] }
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+ assert_equal "1", last_response.body
+
+ # Wait a full second so we have time for changes to propagate
+ sleep(1)
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "2"
+ YAML
+
+ get "/i18n"
+ assert_equal "2", last_response.body
+ end
+
+ test "new locale files are loaded" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [I18n.t(:foo)]] }
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+ assert_equal "1", last_response.body
+
+ # Wait a full second so we have time for changes to propagate
+ sleep(1)
+
+ remove_file "config/locales/en.yml"
+ app_file "config/locales/custom.en.yml", <<-YAML
+en:
+ foo: "2"
+ YAML
+
+ get "/i18n"
+ assert_equal "2", last_response.body
+ end
+
+ test "I18n.load_path is reloaded" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file "config/locales/en.yml", <<-YAML
+en:
+ foo: "1"
+ YAML
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/i18n', :to => lambda { |env| [200, {}, [I18n.load_path.inspect]] }
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+ load_app
+
+ get "/i18n"
+
+ assert_match "en.yml", last_response.body
+
+ # Wait a full second so we have time for changes to propagate
+ sleep(1)
+
+ app_file "config/locales/fr.yml", <<-YAML
+fr:
+ foo: "2"
+ YAML
+
+ get "/i18n"
+ assert_match "fr.yml", last_response.body
+ assert_match "en.yml", last_response.body
+ end
+
+ # Fallbacks
+ test "not using config.i18n.fallbacks does not initialize I18n.fallbacks" do
+ I18n.backend = Class.new(I18n::Backend::Simple).new
+ load_app
+ assert_no_fallbacks
+ end
+
+ test "config.i18n.fallbacks = true initializes I18n.fallbacks with default settings" do
+ I18n::Railtie.config.i18n.fallbacks = true
+ load_app
+ assert I18n.backend.class.included_modules.include?(I18n::Backend::Fallbacks)
+ assert_fallbacks de: [:de, :en]
+ end
+
+ test "config.i18n.fallbacks = true initializes I18n.fallbacks with default settings even when backend changes" do
+ I18n::Railtie.config.i18n.fallbacks = true
+ I18n::Railtie.config.i18n.backend = Class.new(I18n::Backend::Simple).new
+ load_app
+ assert I18n.backend.class.included_modules.include?(I18n::Backend::Fallbacks)
+ assert_fallbacks de: [:de, :en]
+ end
+
+ test "config.i18n.fallbacks.defaults = [:'en-US'] initializes fallbacks with en-US as a fallback default" do
+ I18n::Railtie.config.i18n.fallbacks.defaults = [:'en-US']
+ load_app
+ assert_fallbacks de: [:de, :'en-US', :en]
+ end
+
+ test "config.i18n.fallbacks.map = { :ca => :'es-ES' } initializes fallbacks with a mapping ca => es-ES" do
+ I18n::Railtie.config.i18n.fallbacks.map = { :ca => :'es-ES' }
+ load_app
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :en]
+ end
+
+ test "[shortcut] config.i18n.fallbacks = [:'en-US'] initializes fallbacks with en-US as a fallback default" do
+ I18n::Railtie.config.i18n.fallbacks = [:'en-US']
+ load_app
+ assert_fallbacks de: [:de, :'en-US', :en]
+ end
+
+ test "[shortcut] config.i18n.fallbacks = [{ :ca => :'es-ES' }] initializes fallbacks with a mapping de-AT => de-DE" do
+ I18n::Railtie.config.i18n.fallbacks.map = { :ca => :'es-ES' }
+ load_app
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :en]
+ end
+
+ test "[shortcut] config.i18n.fallbacks = [:'en-US', { :ca => :'es-ES' }] initializes fallbacks with the given arguments" do
+ I18n::Railtie.config.i18n.fallbacks = [:'en-US', { :ca => :'es-ES' }]
+ load_app
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :'en-US', :en]
+ end
+
+ test "disable config.i18n.enforce_available_locales" do
+ add_to_config <<-RUBY
+ config.i18n.enforce_available_locales = false
+ config.i18n.default_locale = :fr
+ RUBY
+
+ load_app
+ assert_equal false, I18n.enforce_available_locales
+
+ assert_nothing_raised do
+ I18n.locale = :es
+ end
+ end
+
+ test "default config.i18n.enforce_available_locales does not override I18n.enforce_available_locales" do
+ I18n.enforce_available_locales = false
+
+ add_to_config <<-RUBY
+ config.i18n.default_locale = :fr
+ RUBY
+
+ load_app
+ assert_equal false, I18n.enforce_available_locales
+
+ assert_nothing_raised do
+ I18n.locale = :es
+ end
+ end
+ end
+end
diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb
new file mode 100644
index 0000000000..cd05956356
--- /dev/null
+++ b/railties/test/application/initializers/load_path_test.rb
@@ -0,0 +1,110 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class LoadPathTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "initializing an application adds the application paths to the load path" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert $:.include?("#{app_path}/app/models")
+ end
+
+ test "initializing an application allows to load code on lib path inside application class definition" do
+ app_file "lib/foo.rb", <<-RUBY
+ module Foo; end
+ RUBY
+
+ add_to_config <<-RUBY
+ require "foo"
+ raise "Expected Foo to be defined" unless defined?(Foo)
+ RUBY
+
+ assert_nothing_raised do
+ require "#{app_path}/config/environment"
+ end
+
+ assert $:.include?("#{app_path}/lib")
+ end
+
+ test "initializing an application eager load any path under app" do
+ app_file "app/anything/foo.rb", <<-RUBY
+ module Foo; end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Foo
+ end
+
+ test "eager loading loads parent classes before children" do
+ app_file "lib/zoo.rb", <<-ZOO
+ class Zoo ; include ReptileHouse ; end
+ ZOO
+
+ app_file "lib/zoo/reptile_house.rb", <<-ZOO
+ module Zoo::ReptileHouse ; end
+ ZOO
+
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.eager_load_paths << "#{app_path}/lib"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Zoo
+ end
+
+ test "eager loading accepts Pathnames" do
+ app_file "lib/foo.rb", <<-RUBY
+ module Foo; end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.eager_load_paths << Pathname.new("#{app_path}/lib")
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert Foo
+ end
+
+ test "load environment with global" do
+ $initialize_test_set_from_env = nil
+ app_file "config/environments/development.rb", <<-RUBY
+ $initialize_test_set_from_env = 'success'
+ Rails.application.configure do
+ config.cache_classes = true
+ config.time_zone = "Brasilia"
+ end
+ RUBY
+
+ assert_nil $initialize_test_set_from_env
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "UTC"
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert_equal "success", $initialize_test_set_from_env
+ assert Rails.application.config.cache_classes
+ assert_equal "Brasilia", Rails.application.config.time_zone
+ end
+ end
+end
diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb
new file mode 100644
index 0000000000..95655b74cf
--- /dev/null
+++ b/railties/test/application/initializers/notifications_test.rb
@@ -0,0 +1,56 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class NotificationsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def instrument(*args, &block)
+ ActiveSupport::Notifications.instrument(*args, &block)
+ end
+
+ def wait
+ ActiveSupport::Notifications.notifier.wait
+ end
+
+ test "rails log_subscribers are added" do
+ add_to_config <<-RUBY
+ config.colorize_logging = false
+ RUBY
+
+ require "#{app_path}/config/environment"
+ require "active_support/log_subscriber/test_helper"
+
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ ActiveRecord::Base.logger = logger
+
+ # Mimic Active Record notifications
+ instrument "sql.active_record", name: "SQL", sql: "SHOW tables"
+ wait
+
+ assert_equal 1, logger.logged(:debug).size
+ assert_match(/SHOW tables/, logger.logged(:debug).last)
+ end
+
+ test 'rails load_config_initializer event is instrumented' do
+ app_file 'config/initializers/foo.rb', ''
+
+ events = []
+ callback = ->(*_) { events << _ }
+ ActiveSupport::Notifications.subscribed(callback, 'load_config_initializer.railties') do
+ app
+ end
+
+ assert_equal %w[load_config_initializer.railties], events.map(&:first)
+ assert_includes events.first.last[:initializer], 'config/initializers/foo.rb'
+ end
+ end
+end
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
new file mode 100644
index 0000000000..2106708c98
--- /dev/null
+++ b/railties/test/application/loading_test.rb
@@ -0,0 +1,372 @@
+require 'isolation/abstract_unit'
+
+class LoadingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "constants in app are autoloaded" do
+ app_file "app/models/post.rb", <<-MODEL
+ class Post < ActiveRecord::Base
+ validates_acceptance_of :title, accept: "omg"
+ end
+ MODEL
+
+ require "#{rails_root}/config/environment"
+ setup_ar!
+
+ p = Post.create(title: 'omg')
+ assert_equal 1, Post.count
+ assert_equal 'omg', p.title
+ p = Post.first
+ assert_equal 'omg', p.title
+ end
+
+ test "concerns in app are autoloaded" do
+ app_file "app/controllers/concerns/trackable.rb", <<-CONCERN
+ module Trackable
+ end
+ CONCERN
+
+ app_file "app/mailers/concerns/email_loggable.rb", <<-CONCERN
+ module EmailLoggable
+ end
+ CONCERN
+
+ app_file "app/models/concerns/orderable.rb", <<-CONCERN
+ module Orderable
+ end
+ CONCERN
+
+ app_file "app/validators/concerns/matchable.rb", <<-CONCERN
+ module Matchable
+ end
+ CONCERN
+
+ require "#{rails_root}/config/environment"
+
+ assert_nothing_raised(NameError) { Trackable }
+ assert_nothing_raised(NameError) { EmailLoggable }
+ assert_nothing_raised(NameError) { Orderable }
+ assert_nothing_raised(NameError) { Matchable }
+ end
+
+ test "models without table do not panic on scope definitions when loaded" do
+ app_file "app/models/user.rb", <<-MODEL
+ class User < ActiveRecord::Base
+ default_scope { where(published: true) }
+ end
+ MODEL
+
+ require "#{rails_root}/config/environment"
+ setup_ar!
+
+ User
+ end
+
+ test "load config/environments/environment before Bootstrap initializers" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.development_environment_loaded = true
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.before_initialize do
+ config.loaded = config.development_environment_loaded
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert ::Rails.application.config.loaded
+ end
+
+ test "descendants loaded after framework initialization are cleaned on each request without cache classes" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.reload_classes_only_on_change = false
+ RUBY
+
+ app_file "app/models/post.rb", <<-MODEL
+ class Post < ActiveRecord::Base
+ end
+ MODEL
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/load', to: lambda { |env| [200, {}, Post.all] }
+ get '/unload', to: lambda { |env| [200, {}, []] }
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+ setup_ar!
+
+ assert_equal [ActiveRecord::SchemaMigration], ActiveRecord::Base.descendants
+ get "/load"
+ assert_equal [ActiveRecord::SchemaMigration, Post], ActiveRecord::Base.descendants
+ get "/unload"
+ assert_equal [ActiveRecord::SchemaMigration], ActiveRecord::Base.descendants
+ end
+
+ test "initialize cant be called twice" do
+ require "#{app_path}/config/environment"
+ assert_raise(RuntimeError) { Rails.application.initialize! }
+ end
+
+ test "reload constants on development" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 1; end
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 2; end
+ end
+ MODEL
+
+ get "/c"
+ assert_equal "2", last_response.body
+ end
+
+ test "does not reload constants on development if custom file watcher always returns false" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.file_watcher = Class.new do
+ def initialize(*); end
+ def updated?; false; end
+ def execute; end
+ def execute_if_updated; false; end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 1; end
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 2; end
+ end
+ MODEL
+
+ get "/c"
+ assert_equal "1", last_response.body
+ end
+
+ test "added files (like db/schema.rb) also trigger reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ $counter ||= 0
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| User.name; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ $counter += 1
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "db/schema.rb", ""
+
+ get "/c"
+ assert_equal "2", last_response.body
+ end
+
+ test "dependencies reloading is followed by routes reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ $counter ||= 1
+ $counter *= 2
+ Rails.application.routes.draw do
+ get '/c', to: lambda { |env| User.name; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ $counter += 1
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "3", last_response.body
+
+ app_file "db/schema.rb", ""
+
+ get "/c"
+ assert_equal "7", last_response.body
+ end
+
+ test "columns migrations also trigger reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get '/title', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.title]] }
+ get '/body', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.body]] }
+ end
+ RUBY
+
+ app_file "app/models/post.rb", <<-MODEL
+ class Post < ActiveRecord::Base
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ app_file "db/migrate/1_create_posts.rb", <<-MIGRATION
+ class CreatePosts < ActiveRecord::Migration
+ def change
+ create_table :posts do |t|
+ t.string :title, default: "TITLE"
+ end
+ end
+ end
+ MIGRATION
+
+ Dir.chdir(app_path) { `rake db:migrate`}
+ require "#{rails_root}/config/environment"
+
+ get "/title"
+ assert_equal "TITLE", last_response.body
+
+ app_file "db/migrate/2_add_body_to_posts.rb", <<-MIGRATION
+ class AddBodyToPosts < ActiveRecord::Migration
+ def change
+ add_column :posts, :body, :text, default: "BODY"
+ end
+ end
+ MIGRATION
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+
+ get "/body"
+ assert_equal "BODY", last_response.body
+ end
+
+ test "AC load hooks can be used with metal" do
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ begin
+ class OmgController < ActionController::Metal
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ def show
+ self.response_body = ["OK"]
+ end
+ end
+ rescue => e
+ puts "Error loading metal: \#{e.class} \#{e.message}"
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ require "#{rails_root}/config/environment"
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get '/omg/show'
+ assert_equal 'OK', last_response.body
+ end
+
+ def test_initialize_can_be_called_at_any_time
+ require "#{app_path}/config/application"
+
+ assert !Rails.initialized?
+ assert !Rails.application.initialized?
+ Rails.initialize!
+ assert Rails.initialized?
+ assert Rails.application.initialized?
+ end
+
+ protected
+
+ def setup_ar!
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ end
+end
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
new file mode 100644
index 0000000000..643d876a26
--- /dev/null
+++ b/railties/test/application/mailer_previews_test.rb
@@ -0,0 +1,701 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+require 'base64'
+
+module ApplicationTests
+ class MailerPreviewsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "/rails/mailers is accessible in development" do
+ app("development")
+ get "/rails/mailers"
+ assert_equal 200, last_response.status
+ end
+
+ test "/rails/mailers is not accessible in production" do
+ app("production")
+ get "/rails/mailers"
+ assert_equal 404, last_response.status
+ end
+
+ test "/rails/mailers is accessible with correct configuraiton" do
+ add_to_config "config.action_mailer.show_previews = true"
+ app("production")
+ get "/rails/mailers", {}, {"REMOTE_ADDR" => "4.2.42.42"}
+ assert_equal 200, last_response.status
+ end
+
+ test "/rails/mailers is not accessible with show_previews = false" do
+ add_to_config "config.action_mailer.show_previews = false"
+ app("development")
+ get "/rails/mailers"
+ assert_equal 404, last_response.status
+ end
+
+ test "/rails/mailers is accessible with globbing route present" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '*foo', to: 'foo#index'
+ end
+ RUBY
+ app("development")
+ get "/rails/mailers"
+ assert_equal 200, last_response.status
+ end
+
+ test "mailer previews are loaded from the default preview_path" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ end
+
+ test "mailer previews are loaded from a custom preview_path" do
+ add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ app_file 'lib/mailer_previews/notifier_preview.rb', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ end
+
+ test "mailer previews are reloaded across requests" do
+ app('development')
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ remove_file 'test/mailers/previews/notifier_preview.rb'
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ end
+
+ test "mailer preview actions are added and removed" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ assert_no_match '<li><a href="/rails/mailers/notifier/bar">bar</a></li>', last_response.body
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+
+ def bar
+ mail to: "to@example.net"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ text_template 'notifier/bar', <<-RUBY
+ Goodbye, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+
+ def bar
+ Notifier.bar
+ end
+ end
+ RUBY
+
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/bar">bar</a></li>', last_response.body
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ remove_file 'app/views/notifier/bar.text.erb'
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ assert_no_match '<li><a href="/rails/mailers/notifier/bar">bar</a></li>', last_response.body
+ end
+
+ test "mailer previews are reloaded from a custom preview_path" do
+ add_to_config "config.action_mailer.preview_path = '#{app_path}/lib/mailer_previews'"
+
+ app('development')
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ app_file 'lib/mailer_previews/notifier_preview.rb', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ get "/rails/mailers"
+ assert_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+
+ remove_file 'lib/mailer_previews/notifier_preview.rb'
+ sleep(1)
+
+ get "/rails/mailers"
+ assert_no_match '<h3><a href="/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ end
+
+ test "mailer preview not found" do
+ app('development')
+ get "/rails/mailers/notifier"
+ assert last_response.not_found?
+ assert_match "Mailer preview &#39;notifier&#39; not found", last_response.body
+ end
+
+ test "mailer preview email not found" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/bar"
+ assert last_response.not_found?
+ assert_match "Email &#39;bar&#39; not found in NotifierPreview", last_response.body
+ end
+
+ test "mailer preview NullMail" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ # does not call +mail+
+ end
+ end
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo"
+ assert_match "You are trying to preview an email that does not have any content.", last_response.body
+ assert_match "notifier#foo", last_response.body
+ end
+
+ test "mailer preview email part not found" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo?part=text%2Fhtml"
+ assert last_response.not_found?
+ assert_match "Email part &#39;text/html&#39; not found in NotifierPreview#foo", last_response.body
+ end
+
+ test "message header uses full display names" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "Ruby on Rails <core@rubyonrails.org>"
+
+ def foo
+ mail to: "Andrew White <andyw@pixeltrix.co.uk>",
+ cc: "David Heinemeier Hansson <david@heinemeierhansson.com>"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match "Ruby on Rails &lt;core@rubyonrails.org&gt;", last_response.body
+ assert_match "Andrew White &lt;andyw@pixeltrix.co.uk&gt;", last_response.body
+ assert_match "David Heinemeier Hansson &lt;david@heinemeierhansson.com&gt;", last_response.body
+ end
+
+ test "part menu selects correct option" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ html_template 'notifier/foo', <<-RUBY
+ <p>Hello, World!</p>
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo.html"
+ assert_equal 200, last_response.status
+ assert_match '<option selected value="?part=text%2Fhtml">View as HTML email</option>', last_response.body
+
+ get "/rails/mailers/notifier/foo.txt"
+ assert_equal 200, last_response.status
+ assert_match '<option selected value="?part=text%2Fplain">View as plain-text email</option>', last_response.body
+ end
+
+ test "mailer previews create correct links when loaded on a subdirectory" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers", {}, 'SCRIPT_NAME' => '/my_app'
+ assert_match '<h3><a href="/my_app/rails/mailers/notifier">Notifier</a></h3>', last_response.body
+ assert_match '<li><a href="/my_app/rails/mailers/notifier/foo">foo</a></li>', last_response.body
+ end
+
+ test "plain text mailer preview with attachment" do
+ image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ attachments['pixel.png'] = File.read("#{app_path}/public/images/pixel.png", mode: 'rb')
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+ end
+
+ test "multipart mailer preview with attachment" do
+ image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ attachments['pixel.png'] = File.read("#{app_path}/public/images/pixel.png", mode: 'rb')
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ html_template 'notifier/foo', <<-RUBY
+ <p>Hello, World!</p>
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/html"
+ assert_equal 200, last_response.status
+ assert_match %r[<p>Hello, World!</p>], last_response.body
+ end
+
+ test "multipart mailer preview with inline attachment" do
+ image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="
+
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ attachments['pixel.png'] = File.read("#{app_path}/public/images/pixel.png", mode: 'rb')
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ html_template 'notifier/foo', <<-RUBY
+ <p>Hello, World!</p>
+ <%= image_tag attachments['pixel.png'].url %>
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/html"
+ assert_equal 200, last_response.status
+ assert_match %r[<p>Hello, World!</p>], last_response.body
+ assert_match %r[src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="], last_response.body
+ end
+
+ test "multipart mailer preview with attached email" do
+ mailer 'notifier', <<-RUBY
+ class Notifier < ActionMailer::Base
+ default from: "from@example.com"
+
+ def foo
+ message = ::Mail.new do
+ from 'foo@example.com'
+ to 'bar@example.com'
+ subject 'Important Message'
+
+ text_part do
+ body 'Goodbye, World!'
+ end
+
+ html_part do
+ body '<p>Goodbye, World!</p>'
+ end
+ end
+
+ attachments['message.eml'] = message.to_s
+ mail to: "to@example.org"
+ end
+ end
+ RUBY
+
+ text_template 'notifier/foo', <<-RUBY
+ Hello, World!
+ RUBY
+
+ html_template 'notifier/foo', <<-RUBY
+ <p>Hello, World!</p>
+ RUBY
+
+ mailer_preview 'notifier', <<-RUBY
+ class NotifierPreview < ActionMailer::Preview
+ def foo
+ Notifier.foo
+ end
+ end
+ RUBY
+
+ app('development')
+
+ get "/rails/mailers/notifier/foo"
+ assert_equal 200, last_response.status
+ assert_match %r[<iframe seamless name="messageBody"], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/plain"
+ assert_equal 200, last_response.status
+ assert_match %r[Hello, World!], last_response.body
+
+ get "/rails/mailers/notifier/foo?part=text/html"
+ assert_equal 200, last_response.status
+ assert_match %r[<p>Hello, World!</p>], last_response.body
+ end
+
+ private
+ def build_app
+ super
+ app_file "config/routes.rb", "Rails.application.routes.draw do; end"
+ end
+
+ def mailer(name, contents)
+ app_file("app/mailers/#{name}.rb", contents)
+ end
+
+ def mailer_preview(name, contents)
+ app_file("test/mailers/previews/#{name}_preview.rb", contents)
+ end
+
+ def html_template(name, contents)
+ app_file("app/views/#{name}.html.erb", contents)
+ end
+
+ def text_template(name, contents)
+ app_file("app/views/#{name}.text.erb", contents)
+ end
+
+ def image_file(name, contents)
+ app_file("public/images/#{name}", Base64.strict_decode64(contents), 'wb')
+ end
+ end
+end
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
new file mode 100644
index 0000000000..c951dabd6c
--- /dev/null
+++ b/railties/test/application/middleware/cache_test.rb
@@ -0,0 +1,180 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class CacheTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ require 'rack/test'
+ extend Rack::Test::Methods
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def simple_controller
+ controller :expires, <<-RUBY
+ class ExpiresController < ApplicationController
+ def expires_header
+ expires_in 10, public: !params[:private]
+ render text: SecureRandom.hex(16)
+ end
+
+ def expires_etag
+ render_conditionally(etag: "1")
+ end
+
+ def expires_last_modified
+ $last_modified ||= Time.now.utc
+ render_conditionally(last_modified: $last_modified)
+ end
+
+ def keeps_if_modified_since
+ render :text => request.headers['If-Modified-Since']
+ end
+ private
+ def render_conditionally(headers)
+ if stale?(headers.merge(public: !params[:private]))
+ render text: SecureRandom.hex(16)
+ end
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+ end
+
+ def test_cache_keeps_if_modified_since
+ simple_controller
+ expected = "Wed, 30 May 1984 19:43:31 GMT"
+
+ get "/expires/keeps_if_modified_since", {}, "HTTP_IF_MODIFIED_SINCE" => expected
+
+ assert_equal 200, last_response.status
+ assert_equal expected, last_response.body, "cache should have kept If-Modified-Since"
+ end
+
+ def test_cache_is_disabled_in_dev_mode
+ simple_controller
+ app("development")
+
+ get "/expires/expires_header"
+ assert_nil last_response.headers['X-Rack-Cache']
+
+ body = last_response.body
+
+ get "/expires/expires_header"
+ assert_nil last_response.headers['X-Rack-Cache']
+ assert_not_equal body, last_response.body
+ end
+
+ def test_cache_works_with_expires
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_header"
+ assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
+ assert_equal "max-age=10, public", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+
+ get "/expires/expires_header"
+
+ assert_equal "fresh", last_response.headers["X-Rack-Cache"]
+
+ assert_equal body, last_response.body
+ end
+
+ def test_cache_works_with_expires_private
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_header", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_equal "private, max-age=10", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+
+ get "/expires/expires_header", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_not_equal body, last_response.body
+ end
+
+ def test_cache_works_with_etags
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_etag"
+ 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
+ assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
+ assert_equal body, last_response.body
+ end
+
+ def test_cache_works_with_etags_private
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_etag", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+ etag = last_response.headers["ETag"]
+
+ get "/expires/expires_etag", {private: true}, "If-None-Match" => etag
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_not_equal body, last_response.body
+ end
+
+ def test_cache_works_with_last_modified
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_last_modified"
+ 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
+ assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
+ assert_equal body, last_response.body
+ end
+
+ def test_cache_works_with_last_modified_private
+ simple_controller
+
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_last_modified", private: true
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
+
+ body = last_response.body
+ last = last_response.headers["Last-Modified"]
+
+ get "/expires/expires_last_modified", {private: true}, "If-Modified-Since" => last
+ assert_equal "miss", last_response.headers["X-Rack-Cache"]
+ assert_not_equal body, last_response.body
+ end
+ end
+end
diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb
new file mode 100644
index 0000000000..bbb7627be9
--- /dev/null
+++ b/railties/test/application/middleware/cookies_test.rb
@@ -0,0 +1,47 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class CookiesTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def new_app
+ File.expand_path("#{app_path}/../new_app")
+ end
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ FileUtils.rm_rf(new_app) if File.directory?(new_app)
+ end
+
+ test 'always_write_cookie is true by default in development' do
+ require 'rails'
+ Rails.env = 'development'
+ require "#{app_path}/config/environment"
+ assert_equal true, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+
+ test 'always_write_cookie is false by default in production' do
+ require 'rails'
+ Rails.env = 'production'
+ require "#{app_path}/config/environment"
+ assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+
+ test 'always_write_cookie can be overridden' do
+ add_to_config <<-RUBY
+ config.action_dispatch.always_write_cookie = false
+ RUBY
+
+ require 'rails'
+ Rails.env = 'development'
+ require "#{app_path}/config/environment"
+ assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+ end
+end
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
new file mode 100644
index 0000000000..7b4babb13b
--- /dev/null
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -0,0 +1,125 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class MiddlewareExceptionsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "show exceptions middleware filter backtrace before logging" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ raise 'oops'
+ end
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal 500, last_response.status
+
+ log = File.read(Rails.application.config.paths["log"].first)
+ assert_no_match(/action_dispatch/, log, log)
+ assert_match(/oops/, log, log)
+ end
+
+ test "renders active record exceptions as 404" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ raise ActiveRecord::RecordNotFound
+ end
+ end
+ RUBY
+
+ get "/foo"
+ assert_equal 404, last_response.status
+ end
+
+ test "uses custom exceptions app" do
+ add_to_config <<-RUBY
+ config.exceptions_app = lambda do |env|
+ [404, { "Content-Type" => "text/plain" }, ["YOU FAILED"]]
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+
+ get "/foo"
+ assert_equal 404, last_response.status
+ assert_equal "YOU FAILED", last_response.body
+ end
+
+ test "url generation error when action_dispatch.show_exceptions is set raises an exception" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ raise ActionController::UrlGenerationError
+ end
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+
+ get '/foo'
+ assert_equal 500, last_response.status
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions is not set raises an exception" do
+ app.config.action_dispatch.show_exceptions = false
+
+ assert_raise(ActionController::RoutingError) do
+ get '/foo'
+ end
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions is set shows 404" do
+ app.config.action_dispatch.show_exceptions = true
+
+ assert_nothing_raised(ActionController::RoutingError) do
+ get '/foo'
+ assert_match "The page you were looking for doesn't exist.", last_response.body
+ end
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ assert_nothing_raised(ActionController::RoutingError) do
+ get '/foo'
+ assert_match "No route matches", last_response.body
+ end
+ end
+
+ test "displays diagnostics message when exception raised in template that contains UTF-8" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ app_file 'app/views/foo/index.html.erb', <<-ERB
+ <% raise 'boooom' %>
+ ✓測試テスト시험
+ ERB
+
+ get '/foo', :utf8 => '✓'
+ assert_match(/boooom/, last_response.body)
+ assert_match(/測試テスト시험/, last_response.body)
+ end
+ end
+end
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
new file mode 100644
index 0000000000..97d5b5c698
--- /dev/null
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -0,0 +1,78 @@
+require 'ipaddr'
+require 'isolation/abstract_unit'
+require 'active_support/key_generator'
+
+module ApplicationTests
+ class RemoteIpTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def remote_ip(env = {})
+ remote_ip = nil
+ env = Rack::MockRequest.env_for("/").merge(env).merge!(
+ 'action_dispatch.show_exceptions' => false,
+ 'action_dispatch.key_generator' => ActiveSupport::LegacyKeyGenerator.new('b3c631c314c0bbca50c1b2843150fe33')
+ )
+
+ endpoint = Proc.new do |e|
+ remote_ip = ActionDispatch::Request.new(e).remote_ip
+ [200, {}, ["Hello"]]
+ end
+
+ Rails.application.middleware.build(endpoint).call(env)
+ remote_ip
+ end
+
+ test "remote_ip works" do
+ make_basic_app
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1")
+ end
+
+ test "checks IP spoofing by default" do
+ make_basic_app
+ assert_raises(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "works with both headers individually" do
+ make_basic_app
+ assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ assert_equal "1.1.1.1", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1")
+ end
+ assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ assert_equal "1.1.1.2", remote_ip("HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "can disable IP spoofing check" do
+ make_basic_app do |app|
+ app.config.action_dispatch.ip_spoofing_check = false
+ end
+
+ assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ assert_equal "1.1.1.1", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "remote_ip works with HTTP_X_FORWARDED_FOR" do
+ make_basic_app
+ assert_equal "4.2.42.42", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "4.2.42.42")
+ end
+
+ test "the user can set trusted proxies" do
+ make_basic_app do |app|
+ app.config.action_dispatch.trusted_proxies = /^4\.2\.42\.42$/
+ end
+
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "4.2.42.42")
+ end
+
+ test "the user can set trusted proxies with an IPAddr argument" do
+ make_basic_app do |app|
+ app.config.action_dispatch.trusted_proxies = IPAddr.new('4.2.42.0/24')
+ end
+
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "10.0.0.0,4.2.42.42")
+ end
+ end
+end
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
new file mode 100644
index 0000000000..be86f1a3b8
--- /dev/null
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -0,0 +1,74 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class SendfileTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ define_method :simple_controller do
+ class ::OmgController < ActionController::Base
+ def index
+ send_file __FILE__
+ end
+ end
+ end
+
+ # x_sendfile_header middleware
+ test "config.action_dispatch.x_sendfile_header defaults to nil" do
+ make_basic_app
+ simple_controller
+
+ get "/"
+ assert !last_response.headers["X-Sendfile"]
+ assert_equal File.read(__FILE__), last_response.body
+ end
+
+ test "config.action_dispatch.x_sendfile_header can be set" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ end
+
+ simple_controller
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Sendfile"]
+ end
+
+ test "config.action_dispatch.x_sendfile_header is sent to Rack::Sendfile" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = 'X-Lighttpd-Send-File'
+ end
+
+ simple_controller
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Lighttpd-Send-File"]
+ end
+
+ test "files handled by ActionDispatch::Static are handled by Rack::Sendfile" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = 'X-Sendfile'
+ app.config.public_file_server.enabled = true
+ app.paths["public"] = File.join(rails_root, "public")
+ end
+
+ app_file "public/foo.txt", "foo"
+
+ get "/foo.txt", "HTTP_X_SENDFILE_TYPE" => "X-Sendfile"
+ assert_equal File.join(rails_root, "public/foo.txt"), last_response.headers["X-Sendfile"]
+ end
+ end
+end
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
new file mode 100644
index 0000000000..25eadfc387
--- /dev/null
+++ b/railties/test/application/middleware/session_test.rb
@@ -0,0 +1,342 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class MiddlewareSessionTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "config.force_ssl sets cookie to secure only" do
+ add_to_config "config.force_ssl = true"
+ require "#{app_path}/config/environment"
+ assert app.config.session_options[:secure], "Expected session to be marked as secure"
+ end
+
+ test "session is not loaded if it's not used" do
+ make_basic_app
+
+ class ::OmgController < ActionController::Base
+ def index
+ if params[:flash]
+ flash[:notice] = "notice"
+ end
+
+ head :ok
+ end
+ end
+
+ get "/?flash=true"
+ get "/"
+
+ assert last_request.env["HTTP_COOKIE"]
+ assert !last_response.headers["Set-Cookie"]
+ end
+
+ test "session is empty and isn't saved on unverified request when using :null_session protect method" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ post ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ protect_from_forgery with: :null_session
+
+ def write_session
+ session[:foo] = 1
+ head :ok
+ end
+
+ def read_session
+ render text: session[:foo].inspect
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.action_controller.allow_forgery_protection = true
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get '/foo/write_session'
+ get '/foo/read_session'
+ assert_equal '1', last_response.body
+
+ post '/foo/read_session' # Read session using POST request without CSRF token
+ assert_equal 'nil', last_response.body # Stored value shouldn't be accessible
+
+ post '/foo/write_session' # Write session using POST request without CSRF token
+ get '/foo/read_session' # Session shouldn't be changed
+ assert_equal '1', last_response.body
+ end
+
+ test "cookie jar is empty and isn't saved on unverified request when using :null_session protect method" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ post ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ protect_from_forgery with: :null_session
+
+ def write_cookie
+ cookies[:foo] = '1'
+ head :ok
+ end
+
+ def read_cookie
+ render text: cookies[:foo].inspect
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.action_controller.allow_forgery_protection = true
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get '/foo/write_cookie'
+ get '/foo/read_cookie'
+ assert_equal '"1"', last_response.body
+
+ post '/foo/read_cookie' # Read cookie using POST request without CSRF token
+ assert_equal 'nil', last_response.body # Stored value shouldn't be accessible
+
+ post '/foo/write_cookie' # Write cookie using POST request without CSRF token
+ get '/foo/read_cookie' # Cookie shouldn't be changed
+ assert_equal '"1"', last_response.body
+ end
+
+ test "session using encrypted cookie store" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def write_session
+ session[:foo] = 1
+ head :ok
+ end
+
+ def read_session
+ render text: session[:foo]
+ end
+
+ def read_encrypted_cookie
+ render text: cookies.encrypted[:_myapp_session]['foo']
+ end
+
+ def read_raw_cookie
+ render text: cookies[:_myapp_session]
+ end
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get '/foo/write_session'
+ get '/foo/read_session'
+ assert_equal '1', last_response.body
+
+ get '/foo/read_encrypted_cookie'
+ assert_equal '1', last_response.body
+
+ secret = app.key_generator.generate_key('encrypted cookie')
+ sign_secret = app.key_generator.generate_key('signed encrypted cookie')
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
+
+ get '/foo/read_raw_cookie'
+ assert_equal 1, encryptor.decrypt_and_verify(last_response.body)['foo']
+ end
+
+ test "session upgrading signature to encryption cookie store works the same way as encrypted cookie store" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def write_session
+ session[:foo] = 1
+ head :ok
+ end
+
+ def read_session
+ render text: session[:foo]
+ end
+
+ def read_encrypted_cookie
+ render text: cookies.encrypted[:_myapp_session]['foo']
+ end
+
+ def read_raw_cookie
+ render text: cookies[:_myapp_session]
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get '/foo/write_session'
+ get '/foo/read_session'
+ assert_equal '1', last_response.body
+
+ get '/foo/read_encrypted_cookie'
+ assert_equal '1', last_response.body
+
+ secret = app.key_generator.generate_key('encrypted cookie')
+ sign_secret = app.key_generator.generate_key('signed encrypted cookie')
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
+
+ get '/foo/read_raw_cookie'
+ assert_equal 1, encryptor.decrypt_and_verify(last_response.body)['foo']
+ end
+
+ test "session upgrading signature to encryption cookie store upgrades session to encrypted mode" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def write_raw_session
+ # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
+ cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
+ head :ok
+ end
+
+ def write_session
+ session[:foo] = session[:foo] + 1
+ head :ok
+ end
+
+ def read_session
+ render text: session[:foo]
+ end
+
+ def read_encrypted_cookie
+ render text: cookies.encrypted[:_myapp_session]['foo']
+ end
+
+ def read_raw_cookie
+ render text: cookies[:_myapp_session]
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get '/foo/write_raw_session'
+ get '/foo/read_session'
+ assert_equal '1', last_response.body
+
+ get '/foo/write_session'
+ get '/foo/read_session'
+ assert_equal '2', last_response.body
+
+ get '/foo/read_encrypted_cookie'
+ assert_equal '2', last_response.body
+
+ secret = app.key_generator.generate_key('encrypted cookie')
+ sign_secret = app.key_generator.generate_key('signed encrypted cookie')
+ encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
+
+ get '/foo/read_raw_cookie'
+ assert_equal 2, encryptor.decrypt_and_verify(last_response.body)['foo']
+ end
+
+ test "session upgrading legacy signed cookies to new signed cookies" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def write_raw_session
+ # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
+ cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
+ head :ok
+ end
+
+ def write_session
+ session[:foo] = session[:foo] + 1
+ head :ok
+ end
+
+ def read_session
+ render text: session[:foo]
+ end
+
+ def read_signed_cookie
+ render text: cookies.signed[:_myapp_session]['foo']
+ end
+
+ def read_raw_cookie
+ render text: cookies[:_myapp_session]
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
+ secrets.secret_key_base = nil
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ get '/foo/write_raw_session'
+ get '/foo/read_session'
+ assert_equal '1', last_response.body
+
+ get '/foo/write_session'
+ get '/foo/read_session'
+ assert_equal '2', last_response.body
+
+ get '/foo/read_signed_cookie'
+ assert_equal '2', last_response.body
+
+ verifier = ActiveSupport::MessageVerifier.new(app.secrets.secret_token)
+
+ get '/foo/read_raw_cookie'
+ assert_equal 2, verifier.verify(last_response.body)['foo']
+ end
+ end
+end
diff --git a/railties/test/application/middleware/static_test.rb b/railties/test/application/middleware/static_test.rb
new file mode 100644
index 0000000000..1246e20d94
--- /dev/null
+++ b/railties/test/application/middleware/static_test.rb
@@ -0,0 +1,68 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class MiddlewareStaticTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # Regression test to #8907
+ # See https://github.com/rails/rails/commit/9cc82b77196d21a5c7021f6dca59ab9b2b158a45#commitcomment-2416514
+ test "doesn't set Cache-Control header when it is nil" do
+ app_file "public/foo.html", 'static'
+
+ require "#{app_path}/config/environment"
+
+ get 'foo'
+
+ assert_not last_response.headers.has_key?('Cache-Control'), "Cache-Control should not be set"
+ end
+
+ test "headers for static files are configurable" do
+ app_file "public/about.html", 'static'
+ add_to_config <<-CONFIG
+ config.public_file_server.headers = {
+ "Access-Control-Allow-Origin" => "http://rubyonrails.org",
+ "Cache-Control" => "public, max-age=60"
+ }
+ CONFIG
+
+ require "#{app_path}/config/environment"
+
+ get '/about.html'
+
+ assert_equal 'http://rubyonrails.org', last_response.headers["Access-Control-Allow-Origin"]
+ assert_equal 'public, max-age=60', last_response.headers["Cache-Control"]
+ end
+
+ test "public_file_server.index_name defaults to 'index'" do
+ app_file "public/index.html", "/index.html"
+
+ require "#{app_path}/config/environment"
+
+ get '/'
+
+ assert_equal "/index.html\n", last_response.body
+ end
+
+ test "public_file_server.index_name configurable" do
+ app_file "public/other-index.html", "/other-index.html"
+ add_to_config "config.public_file_server.index_name = 'other-index'"
+
+ require "#{app_path}/config/environment"
+
+ get '/'
+
+ assert_equal "/other-index.html\n", last_response.body
+ end
+ end
+end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
new file mode 100644
index 0000000000..1434522cce
--- /dev/null
+++ b/railties/test/application/middleware_test.rb
@@ -0,0 +1,298 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class MiddlewareTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "default middleware stack" do
+ add_to_config "config.active_record.migration_error = :page_load"
+
+ boot!
+
+ assert_equal [
+ "Rack::Sendfile",
+ "ActionDispatch::Static",
+ "ActionDispatch::LoadInterlock",
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ "Rack::Runtime",
+ "Rack::MethodOverride",
+ "ActionDispatch::RequestId",
+ "Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods
+ "ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
+ "ActionDispatch::RemoteIp",
+ "ActionDispatch::Reloader",
+ "ActionDispatch::Callbacks",
+ "ActiveRecord::Migration::CheckPending",
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement",
+ "ActiveRecord::QueryCache",
+ "ActionDispatch::Cookies",
+ "ActionDispatch::Session::CookieStore",
+ "ActionDispatch::Flash",
+ "Rack::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag"
+ ], middleware
+ end
+
+ test "api middleware stack" do
+ add_to_config "config.api_only = true"
+
+ boot!
+
+ assert_equal [
+ "Rack::Sendfile",
+ "ActionDispatch::Static",
+ "ActionDispatch::LoadInterlock",
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ "Rack::Runtime",
+ "ActionDispatch::RequestId",
+ "Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods
+ "ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
+ "ActionDispatch::RemoteIp",
+ "ActionDispatch::Reloader",
+ "ActionDispatch::Callbacks",
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement",
+ "ActiveRecord::QueryCache",
+ "Rack::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag"
+ ], middleware
+ end
+
+ test "Rack::Cache is not included by default" do
+ boot!
+
+ assert !middleware.include?("Rack::Cache"), "Rack::Cache is not included in the default stack unless you set config.action_dispatch.rack_cache"
+ end
+
+ test "Rack::Cache is present when action_dispatch.rack_cache is set" do
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ boot!
+
+ assert middleware.include?("Rack::Cache")
+ end
+
+ test "ActiveRecord::Migration::CheckPending is present when active_record.migration_error is set to :page_load" do
+ add_to_config "config.active_record.migration_error = :page_load"
+
+ boot!
+
+ assert middleware.include?("ActiveRecord::Migration::CheckPending")
+ end
+
+ test "ActionDispatch::SSL is present when force_ssl is set" do
+ add_to_config "config.force_ssl = true"
+ boot!
+ assert middleware.include?("ActionDispatch::SSL")
+ end
+
+ test "ActionDispatch::SSL is configured with options when given" do
+ add_to_config "config.force_ssl = true"
+ add_to_config "config.ssl_options = { host: 'example.com' }"
+ boot!
+
+ assert_equal [{host: 'example.com'}], Rails.application.middleware.first.args
+ end
+
+ test "removing Active Record omits its middleware" do
+ use_frameworks []
+ boot!
+ assert !middleware.include?("ActiveRecord::ConnectionAdapters::ConnectionManagement")
+ assert !middleware.include?("ActiveRecord::QueryCache")
+ assert !middleware.include?("ActiveRecord::Migration::CheckPending")
+ end
+
+ test "includes interlock if cache_classes is set but eager_load is not" do
+ add_to_config "config.cache_classes = true"
+ boot!
+ assert_not_includes middleware, "Rack::Lock"
+ assert_includes middleware, "ActionDispatch::LoadInterlock"
+ end
+
+ test "includes interlock if cache_classes is off" do
+ add_to_config "config.cache_classes = false"
+ boot!
+ assert_not_includes middleware, "Rack::Lock"
+ assert_includes middleware, "ActionDispatch::LoadInterlock"
+ end
+
+ test "does not include lock if cache_classes is set and so is eager_load" do
+ add_to_config "config.cache_classes = true"
+ add_to_config "config.eager_load = true"
+ boot!
+ assert_not_includes middleware, "Rack::Lock"
+ assert_not_includes middleware, "ActionDispatch::LoadInterlock"
+ end
+
+ test "does not include lock if allow_concurrency is set to :unsafe" do
+ add_to_config "config.allow_concurrency = :unsafe"
+ boot!
+ assert_not_includes middleware, "Rack::Lock"
+ assert_not_includes middleware, "ActionDispatch::LoadInterlock"
+ end
+
+ test "includes lock if allow_concurrency is disabled" do
+ add_to_config "config.allow_concurrency = false"
+ boot!
+ assert_includes middleware, "Rack::Lock"
+ assert_not_includes middleware, "ActionDispatch::LoadInterlock"
+ end
+
+ test "removes static asset server if public_file_server.enabled is disabled" do
+ add_to_config "config.public_file_server.enabled = false"
+ boot!
+ assert !middleware.include?("ActionDispatch::Static")
+ end
+
+ test "can delete a middleware from the stack" do
+ add_to_config "config.middleware.delete ActionDispatch::Static"
+ boot!
+ assert !middleware.include?("ActionDispatch::Static")
+ end
+
+ test "can delete a middleware from the stack even if insert_before is added after delete" do
+ add_to_config "config.middleware.delete Rack::Runtime"
+ add_to_config "config.middleware.insert_before(Rack::Runtime, Rack::Config)"
+ boot!
+ assert middleware.include?("Rack::Config")
+ assert_not middleware.include?("Rack::Runtime")
+ end
+
+ test "can delete a middleware from the stack even if insert_after is added after delete" do
+ add_to_config "config.middleware.delete Rack::Runtime"
+ add_to_config "config.middleware.insert_after(Rack::Runtime, Rack::Config)"
+ boot!
+ assert middleware.include?("Rack::Config")
+ assert_not middleware.include?("Rack::Runtime")
+ end
+
+ test "includes exceptions middlewares even if action_dispatch.show_exceptions is disabled" do
+ add_to_config "config.action_dispatch.show_exceptions = false"
+ boot!
+ assert middleware.include?("ActionDispatch::ShowExceptions")
+ assert middleware.include?("ActionDispatch::DebugExceptions")
+ end
+
+ test "removes ActionDispatch::Reloader if cache_classes is true" do
+ add_to_config "config.cache_classes = true"
+ boot!
+ assert !middleware.include?("ActionDispatch::Reloader")
+ end
+
+ test "use middleware" do
+ use_frameworks []
+ add_to_config "config.middleware.use Rack::Config"
+ boot!
+ assert_equal "Rack::Config", middleware.last
+ end
+
+ test "insert middleware after" do
+ add_to_config "config.middleware.insert_after Rack::Sendfile, Rack::Config"
+ boot!
+ assert_equal "Rack::Config", middleware.second
+ end
+
+ test 'unshift middleware' do
+ add_to_config 'config.middleware.unshift Rack::Config'
+ boot!
+ assert_equal 'Rack::Config', middleware.first
+ end
+
+ test "Rails.cache does not respond to middleware" do
+ add_to_config "config.cache_store = :memory_store"
+ boot!
+ assert_equal "Rack::Runtime", middleware.fourth
+ end
+
+ test "Rails.cache does respond to middleware" do
+ boot!
+ assert_equal "Rack::Runtime", middleware.fifth
+ end
+
+ test "insert middleware before" do
+ add_to_config "config.middleware.insert_before Rack::Sendfile, Rack::Config"
+ boot!
+ assert_equal "Rack::Config", middleware.first
+ end
+
+ test "can't change middleware after it's built" do
+ boot!
+ assert_raise RuntimeError do
+ app.config.middleware.use Rack::Config
+ end
+ end
+
+ # ConditionalGet + Etag
+ test "conditional get + etag middlewares handle http caching based on body" do
+ make_basic_app
+
+ class ::OmgController < ActionController::Base
+ def index
+ if params[:nothing]
+ render text: ""
+ else
+ render text: "OMG"
+ end
+ end
+ end
+
+ etag = "W/" + "5af83e3196bf99f440f31f2e1a6c9afe".inspect
+
+ get "/"
+ assert_equal 200, last_response.status
+ assert_equal "OMG", last_response.body
+ assert_equal "text/html; charset=utf-8", last_response.headers["Content-Type"]
+ assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
+ assert_equal etag, last_response.headers["Etag"]
+
+ get "/", {}, "HTTP_IF_NONE_MATCH" => etag
+ assert_equal 304, last_response.status
+ assert_equal "", last_response.body
+ assert_equal nil, last_response.headers["Content-Type"]
+ assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
+ assert_equal etag, last_response.headers["Etag"]
+
+ get "/?nothing=true"
+ assert_equal 200, last_response.status
+ assert_equal "", last_response.body
+ assert_equal "text/html; charset=utf-8", last_response.headers["Content-Type"]
+ assert_equal "no-cache", last_response.headers["Cache-Control"]
+ assert_equal nil, last_response.headers["Etag"]
+ end
+
+ test "ORIGINAL_FULLPATH is passed to env" do
+ boot!
+ env = ::Rack::MockRequest.env_for("/foo/?something")
+ Rails.application.call(env)
+
+ assert_equal "/foo/?something", env["ORIGINAL_FULLPATH"]
+ end
+
+ private
+
+ def boot!
+ require "#{app_path}/config/environment"
+ end
+
+ def middleware
+ Rails.application.middleware.map(&:klass).map(&:name)
+ end
+ end
+end
diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb
new file mode 100644
index 0000000000..f2770a9cb4
--- /dev/null
+++ b/railties/test/application/multiple_applications_test.rb
@@ -0,0 +1,176 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class MultipleApplicationsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app(initializers: true)
+ boot_rails
+ require "#{rails_root}/config/environment"
+ Rails.application.config.some_setting = 'something_or_other'
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_cloning_an_application_makes_a_shallow_copy_of_config
+ clone = Rails.application.clone
+
+ assert_equal Rails.application.config, clone.config, "The cloned application should get a copy of the config"
+ assert_equal Rails.application.config.some_setting, clone.config.some_setting, "The some_setting on the config should be the same"
+ end
+
+ def test_inheriting_multiple_times_from_application
+ new_application_class = Class.new(Rails::Application)
+
+ assert_not_equal Rails.application.object_id, new_application_class.instance.object_id
+ end
+
+ def test_initialization_of_multiple_copies_of_same_application
+ application1 = AppTemplate::Application.new
+ application2 = AppTemplate::Application.new
+
+ assert_not_equal Rails.application.object_id, application1.object_id, "New applications should not be the same as the original application"
+ assert_not_equal Rails.application.object_id, application2.object_id, "New applications should not be the same as the original application"
+ end
+
+ def test_initialization_of_application_with_previous_config
+ application1 = AppTemplate::Application.create(config: Rails.application.config)
+ application2 = AppTemplate::Application.create
+
+ assert_equal Rails.application.config, application1.config, "Creating a new application while setting an initial config should result in the same config"
+ assert_not_equal Rails.application.config, application2.config, "New applications without setting an initial config should not have the same config"
+ end
+
+ def test_initialization_of_application_with_previous_railties
+ application1 = AppTemplate::Application.create(railties: Rails.application.railties)
+ application2 = AppTemplate::Application.create
+
+ assert_equal Rails.application.railties, application1.railties
+ assert_not_equal Rails.application.railties, application2.railties
+ end
+
+ def test_initialize_new_application_with_all_previous_initialization_variables
+ application1 = AppTemplate::Application.create(
+ config: Rails.application.config,
+ railties: Rails.application.railties,
+ routes_reloader: Rails.application.routes_reloader,
+ reloaders: Rails.application.reloaders,
+ routes: Rails.application.routes,
+ helpers: Rails.application.helpers,
+ app_env_config: Rails.application.env_config
+ )
+
+ assert_equal Rails.application.config, application1.config
+ assert_equal Rails.application.railties, application1.railties
+ assert_equal Rails.application.routes_reloader, application1.routes_reloader
+ assert_equal Rails.application.reloaders, application1.reloaders
+ assert_equal Rails.application.routes, application1.routes
+ assert_equal Rails.application.helpers, application1.helpers
+ assert_equal Rails.application.env_config, application1.env_config
+ end
+
+ def test_rake_tasks_defined_on_different_applications_go_to_the_same_class
+ run_count = 0
+
+ application1 = AppTemplate::Application.new
+ application1.rake_tasks do
+ run_count += 1
+ end
+
+ application2 = AppTemplate::Application.new
+ application2.rake_tasks do
+ run_count += 1
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert_equal 0, run_count, "The count should stay at zero without any calls to the rake tasks"
+ require 'rake'
+ require 'rake/testtask'
+ require 'rdoc/task'
+ Rails.application.load_tasks
+ assert_equal 2, run_count, "Calling a rake task should result in two increments to the count"
+ end
+
+ def test_multiple_applications_can_be_initialized
+ assert_nothing_raised { AppTemplate::Application.new }
+ end
+
+ def test_initializers_run_on_different_applications_go_to_the_same_class
+ application1 = AppTemplate::Application.new
+ run_count = 0
+
+ AppTemplate::Application.initializer :init0 do
+ run_count += 1
+ end
+
+ application1.initializer :init1 do
+ run_count += 1
+ end
+
+ AppTemplate::Application.new.initializer :init2 do
+ run_count += 1
+ end
+
+ assert_equal 0, run_count, "Without loading the initializers, the count should be 0"
+
+ # Set config.eager_load to false so that an eager_load warning doesn't pop up
+ AppTemplate::Application.create { config.eager_load = false }.initialize!
+
+ assert_equal 3, run_count, "There should have been three initializers that incremented the count"
+ end
+
+ def test_consoles_run_on_different_applications_go_to_the_same_class
+ run_count = 0
+ AppTemplate::Application.console { run_count += 1 }
+ AppTemplate::Application.new.console { run_count += 1 }
+
+ assert_equal 0, run_count, "Without loading the consoles, the count should be 0"
+ Rails.application.load_console
+ assert_equal 2, run_count, "There should have been two consoles that increment the count"
+ end
+
+ def test_generators_run_on_different_applications_go_to_the_same_class
+ run_count = 0
+ AppTemplate::Application.generators { run_count += 1 }
+ AppTemplate::Application.new.generators { run_count += 1 }
+
+ assert_equal 0, run_count, "Without loading the generators, the count should be 0"
+ Rails.application.load_generators
+ assert_equal 2, run_count, "There should have been two generators that increment the count"
+ end
+
+ def test_runners_run_on_different_applications_go_to_the_same_class
+ run_count = 0
+ AppTemplate::Application.runner { run_count += 1 }
+ AppTemplate::Application.new.runner { run_count += 1 }
+
+ assert_equal 0, run_count, "Without loading the runners, the count should be 0"
+ Rails.application.load_runner
+ assert_equal 2, run_count, "There should have been two runners that increment the count"
+ end
+
+ def test_isolate_namespace_on_an_application
+ assert_nil Rails.application.railtie_namespace, "Before isolating namespace, the railtie namespace should be nil"
+ Rails.application.isolate_namespace(AppTemplate)
+ assert_equal Rails.application.railtie_namespace, AppTemplate, "After isolating namespace, we should have a namespace"
+ end
+
+ def test_inserting_configuration_into_application
+ app = AppTemplate::Application.new(config: Rails.application.config)
+ app.config.some_setting = "a_different_setting"
+ assert_equal "a_different_setting", app.config.some_setting, "The configuration's some_setting should be set."
+
+ new_config = Rails::Application::Configuration.new("root_of_application")
+ new_config.some_setting = "some_setting_dude"
+ app.config = new_config
+
+ assert_equal "some_setting_dude", app.config.some_setting, "The configuration's some_setting should have changed."
+ assert_equal "root_of_application", app.config.root, "The root should have changed to the new config's root."
+ assert_equal new_config, app.config, "The application's config should have changed to the new config."
+ end
+ end
+end
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
new file mode 100644
index 0000000000..4029984ce9
--- /dev/null
+++ b/railties/test/application/paths_test.rb
@@ -0,0 +1,83 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class PathsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ app_file "config/environments/development.rb", ""
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.after_initialize do |app|
+ app.config.session_store nil
+ end
+ RUBY
+ require "#{app_path}/config/environment"
+ @paths = Rails.application.config.paths
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def root(*path)
+ app_path(*path).to_s
+ end
+
+ def assert_path(paths, *dir)
+ assert_equal [root(*dir)], paths.expanded
+ end
+
+ def assert_in_load_path(*path)
+ assert $:.any? { |p| File.expand_path(p) == root(*path) }, "Load path does not include '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----"
+ end
+
+ def assert_not_in_load_path(*path)
+ assert !$:.any? { |p| File.expand_path(p) == root(*path) }, "Load path includes '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----"
+ end
+
+ test "booting up Rails yields a valid paths object" do
+ assert_path @paths["app/models"], "app/models"
+ assert_path @paths["app/helpers"], "app/helpers"
+ assert_path @paths["app/views"], "app/views"
+ assert_path @paths["lib"], "lib"
+ assert_path @paths["vendor"], "vendor"
+ assert_path @paths["tmp"], "tmp"
+ assert_path @paths["config"], "config"
+ assert_path @paths["config/locales"], "config/locales/en.yml"
+ assert_path @paths["config/environment"], "config/environment.rb"
+ assert_path @paths["config/environments"], "config/environments/development.rb"
+
+ assert_equal root("app", "controllers"), @paths["app/controllers"].expanded.first
+ end
+
+ test "booting up Rails yields a list of paths that are eager" do
+ eager_load = @paths.eager_load
+ assert eager_load.include?(root("app/controllers"))
+ assert eager_load.include?(root("app/helpers"))
+ assert eager_load.include?(root("app/models"))
+ end
+
+ test "environments has a glob equal to the current environment" do
+ assert_equal "#{Rails.env}.rb", @paths["config/environments"].glob
+ end
+
+ test "load path includes each of the paths in config.paths as long as the directories exist" do
+ assert_in_load_path "app", "controllers"
+ assert_in_load_path "app", "models"
+ assert_in_load_path "app", "helpers"
+ assert_in_load_path "lib"
+ assert_in_load_path "vendor"
+
+ assert_not_in_load_path "app", "views"
+ assert_not_in_load_path "config"
+ assert_not_in_load_path "config", "locales"
+ assert_not_in_load_path "config", "environments"
+ assert_not_in_load_path "tmp"
+ assert_not_in_load_path "tmp", "cache"
+ end
+ end
+end
diff --git a/railties/test/application/per_request_digest_cache_test.rb b/railties/test/application/per_request_digest_cache_test.rb
new file mode 100644
index 0000000000..3198e12662
--- /dev/null
+++ b/railties/test/application/per_request_digest_cache_test.rb
@@ -0,0 +1,63 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+require 'minitest/mock'
+
+require 'action_view'
+require 'active_support/testing/method_call_assertions'
+
+class PerRequestDigestCacheTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include ActiveSupport::Testing::MethodCallAssertions
+ include Rack::Test::Methods
+
+ setup do
+ build_app
+ add_to_config 'config.consider_all_requests_local = true'
+
+ app_file 'app/models/customer.rb', <<-RUBY
+ class Customer < Struct.new(:name, :id)
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ resources :customers, only: :index
+ end
+ RUBY
+
+ app_file 'app/controllers/customers_controller.rb', <<-RUBY
+ class CustomersController < ApplicationController
+ def index
+ render [ Customer.new('david', 1), Customer.new('dingus', 2) ]
+ end
+ end
+ RUBY
+
+ app_file 'app/views/customers/_customer.html.erb', <<-RUBY
+ <% cache customer do %>
+ <%= customer.name %>
+ <% end %>
+ RUBY
+
+ require "#{app_path}/config/environment"
+ end
+
+ teardown :teardown_app
+
+ test "digests are reused when rendering the same template twice" do
+ get '/customers'
+ assert_equal 200, last_response.status
+
+ assert_equal [ '8ba099b7749542fe765ff34a6824d548' ], ActionView::Digestor.cache.values
+ assert_equal %w(david dingus), last_response.body.split.map(&:strip)
+ end
+
+ test "template digests are cleared before a request" do
+ assert_called(ActionView::Digestor.cache, :clear) do
+ get '/customers'
+ assert_equal 200, last_response.status
+ end
+ end
+end
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
new file mode 100644
index 0000000000..0082ec9cd2
--- /dev/null
+++ b/railties/test/application/rack/logger_test.rb
@@ -0,0 +1,56 @@
+require "isolation/abstract_unit"
+require "active_support/log_subscriber/test_helper"
+require "rack/test"
+
+module ApplicationTests
+ module RackTests
+ class LoggerTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include ActiveSupport::LogSubscriber::TestHelper
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ add_to_config <<-RUBY
+ config.logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+ RUBY
+
+ require "#{app_path}/config/environment"
+ super
+ end
+
+ def teardown
+ super
+ teardown_app
+ end
+
+ def logs
+ @logs ||= Rails.logger.logged(:info).join("\n")
+ end
+
+ test "logger logs proper HTTP GET verb and path" do
+ get "/blah"
+ wait
+ assert_match 'Started GET "/blah"', logs
+ end
+
+ test "logger logs proper HTTP HEAD verb and path" do
+ head "/blah"
+ wait
+ assert_match 'Started HEAD "/blah"', logs
+ end
+
+ test "logger logs HTTP verb override" do
+ post "/", _method: 'put'
+ wait
+ assert_match 'Started PUT "/"', logs
+ end
+
+ test "logger logs HEAD requests" do
+ post "/", _method: 'head'
+ wait
+ assert_match 'Started HEAD "/"', logs
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb
new file mode 100644
index 0000000000..49ac9fc66c
--- /dev/null
+++ b/railties/test/application/rackup_test.rb
@@ -0,0 +1,43 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ class RackupTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def rackup
+ require "rack"
+ app, _ = Rack::Builder.parse_file("#{app_path}/config.ru")
+ app
+ end
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "rails app is present" do
+ assert File.exist?(app_path("config"))
+ end
+
+ test "config.ru can be racked up" do
+ Dir.chdir app_path do
+ @app = rackup
+ assert_welcome get("/")
+ end
+ end
+
+ test "Rails.application is available after config.ru has been racked up" do
+ rackup
+ assert_kind_of Rails::Application, Rails.application
+ end
+
+ test "the config object is available on the application object" do
+ rackup
+ assert_equal 'UTC', Rails.application.config.time_zone
+ end
+ end
+end
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
new file mode 100644
index 0000000000..0b0fb50fe1
--- /dev/null
+++ b/railties/test/application/rake/dbs_test.rb
@@ -0,0 +1,313 @@
+require "isolation/abstract_unit"
+require "active_support/core_ext/string/strip"
+
+module ApplicationTests
+ module RakeTests
+ class RakeDbsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def database_url_db_name
+ "db/database_url_db.sqlite3"
+ end
+
+ def set_database_url
+ ENV['DATABASE_URL'] = "sqlite3:#{database_url_db_name}"
+ # ensure it's using the DATABASE_URL
+ FileUtils.rm_rf("#{app_path}/config/database.yml")
+ end
+
+ def db_create_and_drop(expected_database)
+ Dir.chdir(app_path) do
+ output = `bin/rake db:create`
+ assert_empty output
+ assert File.exist?(expected_database)
+ assert_equal expected_database, ActiveRecord::Base.connection_config[:database]
+ output = `bin/rake db:drop`
+ assert_empty output
+ assert !File.exist?(expected_database)
+ end
+ end
+
+ test 'db:create and db:drop without database url' do
+ require "#{app_path}/config/environment"
+ db_create_and_drop ActiveRecord::Base.configurations[Rails.env]['database']
+ end
+
+ test 'db:create and db:drop with database url' do
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_create_and_drop database_url_db_name
+ end
+
+ def with_database_existing
+ Dir.chdir(app_path) do
+ set_database_url
+ `bin/rake db:create`
+ yield
+ `bin/rake db:drop`
+ end
+ end
+
+ test 'db:create failure because database exists' do
+ with_database_existing do
+ output = `bin/rake db:create 2>&1`
+ assert_match(/already exists/, output)
+ assert_equal 0, $?.exitstatus
+ end
+ end
+
+ def with_bad_permissions
+ Dir.chdir(app_path) do
+ set_database_url
+ FileUtils.chmod("-w", "db")
+ yield
+ FileUtils.chmod("+w", "db")
+ end
+ end
+
+ test 'db:create failure because bad permissions' do
+ with_bad_permissions do
+ output = `bin/rake db:create 2>&1`
+ assert_match(/Couldn't create database/, output)
+ assert_equal 1, $?.exitstatus
+ end
+ end
+
+ test 'db:drop failure because database does not exist' do
+ Dir.chdir(app_path) do
+ output = `bin/rake db:drop 2>&1`
+ assert_match(/does not exist/, output)
+ assert_equal 0, $?.exitstatus
+ end
+ end
+
+ test 'db:drop failure because bad permissions' do
+ with_database_existing do
+ with_bad_permissions do
+ output = `bin/rake db:drop 2>&1`
+ assert_match(/Couldn't drop/, output)
+ assert_equal 1, $?.exitstatus
+ end
+ end
+ end
+
+ def db_migrate_and_status(expected_database)
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rake db:migrate`
+ output = `bin/rake db:migrate:status`
+ assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output)
+ assert_match(/up\s+\d{14}\s+Create books/, output)
+ end
+ end
+
+ test 'db:migrate and db:migrate:status without database_url' do
+ require "#{app_path}/config/environment"
+ db_migrate_and_status ActiveRecord::Base.configurations[Rails.env]['database']
+ end
+
+ test 'db:migrate and db:migrate:status with database_url' do
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_migrate_and_status database_url_db_name
+ end
+
+ def db_schema_dump
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rake db:migrate db:schema:dump`
+ schema_dump = File.read("db/schema.rb")
+ assert_match(/create_table \"books\"/, schema_dump)
+ end
+ end
+
+ test 'db:schema:dump without database_url' do
+ db_schema_dump
+ end
+
+ test 'db:schema:dump with database_url' do
+ set_database_url
+ db_schema_dump
+ end
+
+ def db_fixtures_load(expected_database)
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rake db:migrate db:fixtures:load`
+ assert_match expected_database, ActiveRecord::Base.connection_config[:database]
+ require "#{app_path}/app/models/book"
+ assert_equal 2, Book.count
+ end
+ end
+
+ test 'db:fixtures:load without database_url' do
+ require "#{app_path}/config/environment"
+ db_fixtures_load ActiveRecord::Base.configurations[Rails.env]['database']
+ end
+
+ test 'db:fixtures:load with database_url' do
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_fixtures_load database_url_db_name
+ end
+
+ test 'db:fixtures:load with namespaced fixture' do
+ require "#{app_path}/config/environment"
+ Dir.chdir(app_path) do
+ `bin/rails generate model admin::book title:string;
+ bin/rake db:migrate db:fixtures:load`
+ require "#{app_path}/app/models/admin/book"
+ assert_equal 2, Admin::Book.count
+ end
+ end
+
+ def db_structure_dump_and_load(expected_database)
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rake db:migrate db:structure:dump`
+ structure_dump = File.read("db/structure.sql")
+ assert_match(/CREATE TABLE \"books\"/, structure_dump)
+ `bin/rake environment db:drop db:structure:load`
+ assert_match expected_database, ActiveRecord::Base.connection_config[:database]
+ require "#{app_path}/app/models/book"
+ #if structure is not loaded correctly, exception would be raised
+ assert_equal 0, Book.count
+ end
+ end
+
+ test 'db:structure:dump and db:structure:load without database_url' do
+ require "#{app_path}/config/environment"
+ db_structure_dump_and_load ActiveRecord::Base.configurations[Rails.env]['database']
+ end
+
+ test 'db:structure:dump and db:structure:load with database_url' do
+ require "#{app_path}/config/environment"
+ set_database_url
+ db_structure_dump_and_load database_url_db_name
+ end
+
+ test 'db:structure:dump does not dump schema information when no migrations are used' do
+ Dir.chdir(app_path) do
+ # create table without migrations
+ `bin/rails runner 'ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }'`
+
+ stderr_output = capture(:stderr) { `bin/rake db:structure:dump` }
+ assert_empty stderr_output
+ structure_dump = File.read("db/structure.sql")
+ assert_match(/CREATE TABLE \"posts\"/, structure_dump)
+ end
+ end
+
+ test 'db:schema:load and db:structure:load do not purge the existing database' do
+ Dir.chdir(app_path) do
+ `bin/rails runner 'ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }'`
+
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table(:comments) {}
+ end
+ RUBY
+
+ list_tables = lambda { `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip }
+
+ assert_equal '["posts"]', list_tables[]
+ `bin/rake db:schema:load`
+ assert_equal '["posts", "comments", "schema_migrations"]', list_tables[]
+
+ app_file 'db/structure.sql', <<-SQL
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
+ SQL
+
+ `bin/rake db:structure:load`
+ assert_equal '["posts", "comments", "schema_migrations", "users"]', list_tables[]
+ end
+ end
+
+ test "db:schema:load with inflections" do
+ Dir.chdir(app_path) do
+ app_file 'config/initializers/inflection.rb', <<-RUBY
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular 'goose', 'geese'
+ end
+ RUBY
+ app_file 'config/initializers/primary_key_table_name.rb', <<-RUBY
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
+ RUBY
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: 20140423102712) do
+ create_table("goose".pluralize) do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ `bin/rake db:schema:load`
+
+ tables = `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip
+ assert_match(/"geese"/, tables)
+
+ columns = `bin/rails runner 'p ActiveRecord::Base.connection.columns("geese").map(&:name)'`.strip
+ assert_equal columns, '["gooseid", "name"]'
+ end
+ end
+
+ def db_test_load_structure
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rake db:migrate db:structure:dump db:test:load_structure`
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
+ ActiveRecord::Base.establish_connection :test
+ require "#{app_path}/app/models/book"
+ #if structure is not loaded correctly, exception would be raised
+ assert_equal 0, Book.count
+ assert_match ActiveRecord::Base.configurations['test']['database'],
+ ActiveRecord::Base.connection_config[:database]
+ end
+ end
+
+ test 'db:test:load_structure without database_url' do
+ require "#{app_path}/config/environment"
+ db_test_load_structure
+ end
+
+ test 'db:setup loads schema and seeds database' do
+ begin
+ @old_rails_env = ENV["RAILS_ENV"]
+ @old_rack_env = ENV["RACK_ENV"]
+ ENV.delete "RAILS_ENV"
+ ENV.delete "RACK_ENV"
+
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: "1") do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ app_file 'db/seeds.rb', <<-RUBY
+ puts ActiveRecord::Base.connection_config[:database]
+ RUBY
+
+ Dir.chdir(app_path) do
+ database_path = `bin/rake db:setup`
+ assert_equal "development.sqlite3", File.basename(database_path.strip)
+ end
+ ensure
+ ENV["RAILS_ENV"] = @old_rails_env
+ ENV["RACK_ENV"] = @old_rack_env
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/framework_test.rb b/railties/test/application/rake/framework_test.rb
new file mode 100644
index 0000000000..ec57af79f6
--- /dev/null
+++ b/railties/test/application/rake/framework_test.rb
@@ -0,0 +1,48 @@
+require "isolation/abstract_unit"
+require "active_support/core_ext/string/strip"
+
+module ApplicationTests
+ module RakeTests
+ class FrameworkTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def load_tasks
+ require 'rake'
+ require 'rdoc/task'
+ require 'rake/testtask'
+
+ Rails.application.load_tasks
+ end
+
+ test 'requiring the rake task should not define method .app_generator on Object' do
+ require "#{app_path}/config/environment"
+
+ load_tasks
+
+ assert_raise NameError do
+ Object.method(:app_generator)
+ end
+ end
+
+ test 'requiring the rake task should not define method .invoke_from_app_generator on Object' do
+ require "#{app_path}/config/environment"
+
+ load_tasks
+
+ assert_raise NameError do
+ Object.method(:invoke_from_app_generator)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb
new file mode 100644
index 0000000000..6b74707959
--- /dev/null
+++ b/railties/test/application/rake/migrations_test.rb
@@ -0,0 +1,228 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeMigrationsTest < ActiveSupport::TestCase
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test 'running migrations with given scope' do
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string`
+
+ app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION
+ class AMigration < ActiveRecord::Migration
+ end
+ MIGRATION
+
+ output = `bin/rake db:migrate SCOPE=bukkits`
+ assert_no_match(/create_table\(:users\)/, output)
+ assert_no_match(/CreateUsers/, output)
+ assert_no_match(/add_column\(:users, :email, :string\)/, output)
+
+ assert_match(/AMigration: migrated/, output)
+
+ output = `bin/rake db:migrate SCOPE=bukkits VERSION=0`
+ assert_no_match(/drop_table\(:users\)/, output)
+ assert_no_match(/CreateUsers/, output)
+ assert_no_match(/remove_column\(:users, :email\)/, output)
+
+ assert_match(/AMigration: reverted/, 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;
+ bin/rails generate migration add_email_to_users email:string`
+
+ output = `bin/rake db:migrate`
+ assert_match(/create_table\(:users\)/, output)
+ assert_match(/CreateUsers: migrated/, output)
+ assert_match(/add_column\(:users, :email, :string\)/, output)
+ assert_match(/AddEmailToUsers: migrated/, output)
+
+ output = `bin/rake db:rollback STEP=2`
+ assert_match(/drop_table\(:users\)/, output)
+ assert_match(/CreateUsers: reverted/, output)
+ assert_match(/remove_column\(:users, :email, :string\)/, output)
+ assert_match(/AddEmailToUsers: reverted/, output)
+ end
+ end
+
+ test 'migration status when schema migrations table is not present' do
+ output = Dir.chdir(app_path){ `bin/rake db:migrate:status 2>&1` }
+ assert_equal "Schema migrations table does not exist yet.\n", output
+ end
+
+ test '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;
+ bin/rake db:migrate`
+
+ output = `bin/rake 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/rake db:rollback STEP=1`
+ output = `bin/rake db:migrate:status`
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+ end
+ end
+
+ test 'migration status without timestamps' do
+ add_to_config('config.active_record.timestamped_migrations = false')
+
+ 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/rake db:migrate`
+
+ output = `bin/rake db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+
+ `bin/rake db:rollback STEP=1`
+ output = `bin/rake db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/down\s+\d{3,}\s+Add email to users/, output)
+ end
+ end
+
+ test '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;
+ bin/rake db:migrate`
+
+ output = `bin/rake 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/rake db:rollback STEP=2`
+ output = `bin/rake 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/rake db:migrate:redo`
+ output = `bin/rake 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 'migration status after rollback and redo without timestamps' do
+ add_to_config('config.active_record.timestamped_migrations = false')
+
+ 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/rake db:migrate`
+
+ output = `bin/rake db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+
+ `bin/rake db:rollback STEP=2`
+ output = `bin/rake db:migrate:status`
+
+ assert_match(/down\s+\d{3,}\s+Create users/, output)
+ assert_match(/down\s+\d{3,}\s+Add email to users/, output)
+
+ `bin/rake db:migrate:redo`
+ output = `bin/rake db:migrate:status`
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+ end
+ end
+
+ test 'running migrations with not timestamp head migration files' do
+ Dir.chdir(app_path) do
+
+ app_file "db/migrate/1_one_migration.rb", <<-MIGRATION
+ class OneMigration < ActiveRecord::Migration
+ end
+ MIGRATION
+
+ app_file "db/migrate/02_two_migration.rb", <<-MIGRATION
+ class TwoMigration < ActiveRecord::Migration
+ end
+ MIGRATION
+
+ `bin/rake db:migrate`
+
+ output = `bin/rake db:migrate:status`
+
+ assert_match(/up\s+001\s+One migration/, output)
+ assert_match(/up\s+002\s+Two migration/, output)
+ end
+ end
+
+ test 'schema generation when dump_schema_after_migration is set' do
+ add_to_config('config.active_record.dump_schema_after_migration = false')
+
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string`
+ output = `bin/rails generate model author name:string`
+ version = output =~ %r{[^/]+db/migrate/(\d+)_create_authors\.rb} && $1
+
+ `bin/rake db:migrate db:rollback db:forward db:migrate:up db:migrate:down VERSION=#{version}`
+ assert !File.exist?("db/schema.rb"), "should not dump schema when configured not to"
+ end
+
+ add_to_config('config.active_record.dump_schema_after_migration = true')
+
+ Dir.chdir(app_path) do
+ `bin/rails generate model reviews book_id:integer`
+ `bin/rake db:migrate`
+
+ structure_dump = File.read("db/schema.rb")
+ assert_match(/create_table "reviews"/, structure_dump)
+ end
+ end
+
+ test 'default schema generation after migration' do
+ Dir.chdir(app_path) do
+ `bin/rails generate model book title:string;
+ bin/rake db:migrate`
+
+ structure_dump = File.read("db/schema.rb")
+ assert_match(/create_table "books"/, structure_dump)
+ end
+ end
+
+ test '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;
+ bin/rake db:migrate
+ rm db/migrate/*email*.rb`
+
+ output = `bin/rake 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)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
new file mode 100644
index 0000000000..c87515f00f
--- /dev/null
+++ b/railties/test/application/rake/notes_test.rb
@@ -0,0 +1,157 @@
+require "isolation/abstract_unit"
+require 'rails/source_annotation_extractor'
+
+module ApplicationTests
+ module RakeTests
+ class RakeNotesTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ require "rails/all"
+ super
+ end
+
+ def teardown
+ super
+ teardown_app
+ end
+
+ test 'notes finds notes for certain file_types' do
+ app_file "app/views/home/index.html.erb", "<% # TODO: note in erb %>"
+ app_file "app/assets/javascripts/application.js", "// TODO: note in js"
+ app_file "app/assets/stylesheets/application.css", "// TODO: note in css"
+ app_file "app/controllers/application_controller.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in ruby"
+ app_file "lib/tasks/task.rake", "# TODO: note in rake"
+ app_file 'app/views/home/index.html.builder', '# TODO: note in builder'
+ app_file 'config/locales/en.yml', '# TODO: note in yml'
+ app_file 'config/locales/en.yaml', '# TODO: note in yaml'
+ app_file "app/views/home/index.ruby", "# TODO: note in ruby"
+
+ run_rake_notes do |output, lines|
+ assert_match(/note in erb/, output)
+ assert_match(/note in js/, output)
+ assert_match(/note in css/, output)
+ assert_match(/note in rake/, output)
+ assert_match(/note in builder/, output)
+ assert_match(/note in yml/, output)
+ assert_match(/note in yaml/, output)
+ assert_match(/note in ruby/, output)
+
+ assert_equal 9, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test 'notes finds notes in default directories' do
+ app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
+ app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
+ app_file "db/some_seeds.rb", "# TODO: note in db directory"
+ app_file "lib/some_file.rb", "# TODO: note in lib directory"
+ app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
+
+ app_file "some_other_dir/blah.rb", "# TODO: note in some_other directory"
+
+ run_rake_notes do |output, lines|
+ assert_match(/note in app directory/, output)
+ assert_match(/note in config directory/, output)
+ assert_match(/note in db directory/, output)
+ assert_match(/note in lib directory/, output)
+ assert_match(/note in test directory/, output)
+ assert_no_match(/note in some_other directory/, output)
+
+ assert_equal 5, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test 'notes finds notes in custom directories' do
+ app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
+ app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
+ app_file "db/some_seeds.rb", "# TODO: note in db directory"
+ app_file "lib/some_file.rb", "# TODO: note in lib directory"
+ app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
+
+ app_file "some_other_dir/blah.rb", "# TODO: note in some_other directory"
+
+ run_rake_notes "SOURCE_ANNOTATION_DIRECTORIES='some_other_dir' bin/rake notes" do |output, lines|
+ assert_match(/note in app directory/, output)
+ assert_match(/note in config directory/, output)
+ assert_match(/note in db directory/, output)
+ assert_match(/note in lib directory/, output)
+ assert_match(/note in test directory/, output)
+
+ assert_match(/note in some_other directory/, output)
+
+ assert_equal 6, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test 'custom rake task finds specific notes in specific directories' do
+ app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
+ app_file "lib/some_file.rb", "# OPTIMIZE: note in lib directory\n" << "# FIXME: note in lib directory"
+ app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
+
+ app_file "lib/tasks/notes_custom.rake", <<-EOS
+ require 'rails/source_annotation_extractor'
+ task :notes_custom do
+ tags = 'TODO|FIXME'
+ opts = { dirs: %w(lib test), tag: true }
+ SourceAnnotationExtractor.enumerate(tags, opts)
+ end
+ EOS
+
+ run_rake_notes "bin/rake notes_custom" do |output, lines|
+ assert_match(/\[FIXME\] note in lib directory/, output)
+ assert_match(/\[TODO\] note in test directory/, output)
+ assert_no_match(/OPTIMIZE/, output)
+ assert_no_match(/note in app directory/, output)
+
+ assert_equal 2, lines.size
+ assert_equal [4], lines.map(&:size).uniq
+ end
+ end
+
+ test 'register a new extension' do
+ add_to_config "config.assets.precompile = []"
+ add_to_config %q{ config.annotations.register_extensions("scss", "sass") { |annotation| /\/\/\s*(#{annotation}):?\s*(.*)$/ } }
+ app_file "app/assets/stylesheets/application.css.scss", "// TODO: note in scss"
+ app_file "app/assets/stylesheets/application.css.sass", "// TODO: note in sass"
+
+ run_rake_notes do |output, lines|
+ assert_match(/note in scss/, output)
+ assert_match(/note in sass/, output)
+ assert_equal 2, lines.size
+ end
+ end
+
+ private
+
+ def run_rake_notes(command = 'bin/rake notes')
+ boot_rails
+ load_tasks
+
+ Dir.chdir(app_path) do
+ output = `#{command}`
+ lines = output.scan(/\[([0-9\s]+)\]\s/).flatten
+
+ yield output, lines
+ end
+ end
+
+ def load_tasks
+ require 'rake'
+ require 'rdoc/task'
+ require 'rake/testtask'
+
+ Rails.application.load_tasks
+ end
+
+ def boot_rails
+ super
+ require "#{app_path}/config/environment"
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/restart_test.rb b/railties/test/application/rake/restart_test.rb
new file mode 100644
index 0000000000..4cae199e6b
--- /dev/null
+++ b/railties/test/application/rake/restart_test.rb
@@ -0,0 +1,39 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeRestartTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test 'rake restart touches tmp/restart.txt' do
+ Dir.chdir(app_path) do
+ `rake restart`
+ assert File.exist?("tmp/restart.txt")
+
+ prev_mtime = File.mtime("tmp/restart.txt")
+ sleep(1)
+ `rake restart`
+ curr_mtime = File.mtime("tmp/restart.txt")
+ assert_not_equal prev_mtime, curr_mtime
+ end
+ end
+
+ test 'rake restart should work even if tmp folder does not exist' do
+ Dir.chdir(app_path) do
+ FileUtils.remove_dir('tmp')
+ `rake restart`
+ assert File.exist?('tmp/restart.txt')
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
new file mode 100644
index 0000000000..0da0928b48
--- /dev/null
+++ b/railties/test/application/rake_test.rb
@@ -0,0 +1,314 @@
+require "isolation/abstract_unit"
+require "active_support/core_ext/string/strip"
+
+module ApplicationTests
+ class RakeTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_gems_tasks_are_loaded_first_than_application_ones
+ app_file "lib/tasks/app.rake", <<-RUBY
+ $task_loaded = Rake::Task.task_defined?("db:create:all")
+ RUBY
+
+ require "#{app_path}/config/environment"
+ ::Rails.application.load_tasks
+ assert $task_loaded
+ end
+
+ def test_environment_is_required_in_rake_tasks
+ app_file "config/environment.rb", <<-RUBY
+ SuperMiddleware = Struct.new(:app)
+
+ Rails.application.configure do
+ config.middleware.use SuperMiddleware
+ end
+
+ Rails.application.initialize!
+ RUBY
+
+ assert_match("SuperMiddleware", Dir.chdir(app_path){ `bin/rake middleware` })
+ end
+
+ def test_initializers_are_executed_in_rake_tasks
+ add_to_config <<-RUBY
+ initializer "do_something" do
+ puts "Doing something..."
+ end
+
+ rake_tasks do
+ task do_nothing: :environment do
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path){ `bin/rake do_nothing` }
+ assert_match "Doing something...", output
+ end
+
+ def test_does_not_explode_when_accessing_a_model
+ add_to_config <<-RUBY
+ rake_tasks do
+ task do_nothing: :environment do
+ Hello.new.world
+ end
+ end
+ RUBY
+
+ app_file 'app/models/hello.rb', <<-RUBY
+ class Hello
+ def world
+ puts 'Hello world'
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rake do_nothing` }
+ assert_match 'Hello world', output
+ end
+
+ def test_should_not_eager_load_model_for_rake
+ add_to_config <<-RUBY
+ rake_tasks do
+ task do_nothing: :environment do
+ end
+ end
+ RUBY
+
+ add_to_env_config 'production', <<-RUBY
+ config.eager_load = true
+ RUBY
+
+ app_file 'app/models/hello.rb', <<-RUBY
+ raise 'should not be pre-required for rake even eager_load=true'
+ RUBY
+
+ Dir.chdir(app_path) do
+ assert system('bin/rake do_nothing RAILS_ENV=production'),
+ 'should not be pre-required for rake even eager_load=true'
+ end
+ end
+
+ def test_code_statistics_sanity
+ assert_match "Code LOC: 7 Test LOC: 0 Code to Test Ratio: 1:0.0",
+ Dir.chdir(app_path){ `bin/rake stats` }
+ end
+
+ def test_rake_routes_calls_the_route_inspector
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ end
+ RUBY
+
+ output = Dir.chdir(app_path){ `bin/rake routes` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ end
+
+ def test_rake_routes_with_controller_environment
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ get '/basketball', to: 'basketball#index'
+ end
+ RUBY
+
+ ENV['CONTROLLER'] = 'cart'
+ output = Dir.chdir(app_path){ `bin/rake routes` }
+ assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ end
+
+ def test_rake_routes_displays_message_when_no_routes_are_defined
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ end
+ RUBY
+
+ assert_equal <<-MESSAGE.strip_heredoc, Dir.chdir(app_path){ `bin/rake routes` }
+ You don't have any routes defined!
+
+ Please add some routes in config/routes.rb.
+
+ For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
+ MESSAGE
+ end
+
+ def test_logger_is_flushed_when_exiting_production_rake_tasks
+ add_to_config <<-RUBY
+ rake_tasks do
+ task log_something: :environment do
+ Rails.logger.error("Sample log message")
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path){ `bin/rake log_something RAILS_ENV=production && cat log/production.log` }
+ assert_match "Sample log message", output
+ end
+
+ def test_loading_specific_fixtures
+ Dir.chdir(app_path) do
+ `bin/rails generate model user username:string password:string;
+ bin/rails generate model product name:string;
+ bin/rake db:migrate`
+ end
+
+ require "#{rails_root}/config/environment"
+
+ # loading a specific fixture
+ errormsg = Dir.chdir(app_path) { `bin/rake db:fixtures:load FIXTURES=products` }
+ assert $?.success?, errormsg
+
+ assert_equal 2, ::AppTemplate::Application::Product.count
+ assert_equal 0, ::AppTemplate::Application::User.count
+ end
+
+ def test_loading_only_yml_fixtures
+ Dir.chdir(app_path) do
+ `bin/rake db:migrate`
+ end
+
+ app_file "test/fixtures/products.csv", ""
+
+ require "#{rails_root}/config/environment"
+ errormsg = Dir.chdir(app_path) { `bin/rake db:fixtures:load` }
+ assert $?.success?, errormsg
+ end
+
+ def test_scaffold_tests_pass_by_default
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold user username:string password:string;
+ bin/rake db:migrate test`
+ end
+
+ assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
+ def test_api_scaffold_tests_pass_by_default
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold user username:string password:string;
+ bin/rake db:migrate test`
+ end
+
+ assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
+ def test_scaffold_with_references_columns_tests_pass_when_belongs_to_is_optional
+ app_file "config/initializers/active_record_belongs_to_required_by_default.rb",
+ "Rails.application.config.active_record.belongs_to_required_by_default = false"
+
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold LineItems product:references cart:belongs_to;
+ bin/rake db:migrate test`
+ end
+
+ assert_match(/7 runs, 12 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
+ def test_db_test_clone_when_using_sql_format
+ add_to_config "config.active_record.schema_format = :sql"
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold user username:string;
+ bin/rake db:migrate;
+ bin/rake db:test:clone 2>&1 --trace`
+ end
+ assert_match(/Execute db:test:clone_structure/, output)
+ end
+
+ def test_db_test_prepare_when_using_sql_format
+ add_to_config "config.active_record.schema_format = :sql"
+ output = Dir.chdir(app_path) do
+ `bin/rails generate scaffold user username:string;
+ bin/rake db:migrate;
+ bin/rake db:test:prepare 2>&1 --trace`
+ end
+ assert_match(/Execute db:test:load_structure/, output)
+ end
+
+ def test_rake_dump_structure_should_respect_db_structure_env_variable
+ Dir.chdir(app_path) do
+ # ensure we have a schema_migrations table to dump
+ `bin/rake db:migrate db:structure:dump SCHEMA=db/my_structure.sql`
+ end
+ assert File.exist?(File.join(app_path, 'db', 'my_structure.sql'))
+ end
+
+ def test_rake_dump_structure_should_be_called_twice_when_migrate_redo
+ add_to_config "config.active_record.schema_format = :sql"
+
+ output = Dir.chdir(app_path) do
+ `bin/rails g model post title:string;
+ bin/rake db:migrate:redo 2>&1 --trace;`
+ end
+
+ # expect only Invoke db:structure:dump (first_time)
+ assert_no_match(/^\*\* Invoke db:structure:dump\s+$/, output)
+ end
+
+ def test_rake_dump_schema_cache
+ Dir.chdir(app_path) do
+ `bin/rails generate model post title:string;
+ bin/rails generate model product name:string;
+ bin/rake db:migrate db:schema:cache:dump`
+ end
+ assert File.exist?(File.join(app_path, 'db', 'schema_cache.dump'))
+ end
+
+ def test_rake_clear_schema_cache
+ Dir.chdir(app_path) do
+ `bin/rake db:schema:cache:dump db:schema:cache:clear`
+ end
+ assert !File.exist?(File.join(app_path, 'db', 'schema_cache.dump'))
+ end
+
+ def test_copy_templates
+ Dir.chdir(app_path) do
+ `bin/rake rails:templates:copy`
+ %w(controller mailer scaffold).each do |dir|
+ assert File.exist?(File.join(app_path, 'lib', 'templates', 'erb', dir))
+ end
+ %w(controller helper scaffold_controller assets).each do |dir|
+ assert File.exist?(File.join(app_path, 'lib', 'templates', 'rails', dir))
+ end
+ end
+ end
+
+ def test_template_load_initializers
+ app_file "config/initializers/dummy.rb", "puts 'Hello, World!'"
+ app_file "template.rb", ""
+
+ output = Dir.chdir(app_path) do
+ `bin/rake rails:template LOCATION=template.rb`
+ end
+
+ assert_match(/Hello, World!/, output)
+ end
+
+ def test_tmp_clear_should_work_if_folder_missing
+ FileUtils.remove_dir("#{app_path}/tmp")
+ errormsg = Dir.chdir(app_path) { `bin/rake tmp:clear` }
+ assert_predicate $?, :success?
+ assert_empty errormsg
+ end
+ end
+end
diff --git a/railties/test/application/rendering_test.rb b/railties/test/application/rendering_test.rb
new file mode 100644
index 0000000000..b01febd768
--- /dev/null
+++ b/railties/test/application/rendering_test.rb
@@ -0,0 +1,45 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class RoutingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "Unknown format falls back to HTML template" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'pages/:id', to: 'pages#show'
+ end
+ RUBY
+
+ app_file 'app/controllers/pages_controller.rb', <<-RUBY
+ class PagesController < ApplicationController
+ layout false
+
+ def show
+ end
+ end
+ RUBY
+
+ app_file 'app/views/pages/show.html.erb', <<-RUBY
+ <%= params[:id] %>
+ RUBY
+
+ get '/pages/foo'
+ assert_equal 200, last_response.status
+
+ get '/pages/foo.bar'
+ assert_equal 200, last_response.status
+ end
+ end
+end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
new file mode 100644
index 0000000000..0777714d35
--- /dev/null
+++ b/railties/test/application/routing_test.rb
@@ -0,0 +1,480 @@
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class RoutingTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "rails/welcome in development" do
+ app("development")
+ get "/"
+ assert_equal 200, last_response.status
+ end
+
+ test "rails/info in development" do
+ app("development")
+ get "/rails/info"
+ assert_equal 302, last_response.status
+ end
+
+ test "rails/info/routes in development" do
+ app("development")
+ get "/rails/info/routes"
+ assert_equal 200, last_response.status
+ end
+
+ test "rails/info/properties in development" do
+ app("development")
+ get "/rails/info/properties"
+ assert_equal 200, last_response.status
+ end
+
+ test "root takes precedence over internal welcome controller" do
+ app("development")
+
+ get '/'
+ assert_match %r{<h1>Getting started</h1>} , last_response.body
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render text: "foo"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ root to: "foo#index"
+ end
+ RUBY
+
+ get '/'
+ assert_equal 'foo', last_response.body
+ end
+
+ test "rails/welcome in production" do
+ app("production")
+ get "/"
+ assert_equal 404, last_response.status
+ end
+
+ test "rails/info in production" do
+ app("production")
+ get "/rails/info"
+ assert_equal 404, last_response.status
+ end
+
+ test "rails/info/routes in production" do
+ app("production")
+ get "/rails/info/routes"
+ assert_equal 404, last_response.status
+ end
+
+ test "rails/info/properties in production" do
+ app("production")
+ get "/rails/info/properties"
+ assert_equal 404, last_response.status
+ end
+
+ test "simple controller" do
+ simple_controller
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+ end
+
+ test "simple controller with helper" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render inline: "<%= foo_or_bar? %>"
+ end
+ end
+ RUBY
+
+ app_file 'app/helpers/bar_helper.rb', <<-RUBY
+ module BarHelper
+ def foo_or_bar?
+ "bar"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ get '/foo'
+ assert_equal 'bar', last_response.body
+ end
+
+ test "mount rack app" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ mount lambda { |env| [200, {}, [env["PATH_INFO"]]] }, at: "/blog"
+ # The line below is required because mount sometimes
+ # fails when a resource route is added.
+ resource :user
+ end
+ RUBY
+
+ get '/blog/archives'
+ assert_equal '/archives', last_response.body
+ end
+
+ test "mount named rack app" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render text: my_blog_path
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ mount lambda { |env| [200, {}, [env["PATH_INFO"]]] }, at: "/blog", as: "my_blog"
+ get '/foo' => 'foo#index'
+ end
+ RUBY
+
+ get '/foo'
+ assert_equal '/blog', last_response.body
+ end
+
+ test "multiple controllers" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render text: "foo"
+ end
+ end
+ RUBY
+
+ controller :bar, <<-RUBY
+ class BarController < ActionController::Base
+ def index
+ render text: "bar"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+
+ get '/bar'
+ assert_equal 'bar', last_response.body
+ end
+
+ test "nested controller" do
+ controller 'foo', <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render text: "foo"
+ end
+ end
+ RUBY
+
+ controller 'admin/foo', <<-RUBY
+ module Admin
+ class FooController < ApplicationController
+ def index
+ render text: "admin::foo"
+ end
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'admin/foo', to: 'admin/foo#index'
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+
+ get '/admin/foo'
+ assert_equal 'admin::foo', last_response.body
+ end
+
+ test "routes appending blocks" do
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller/:action'
+ end
+ RUBY
+
+ add_to_config <<-R
+ routes.append do
+ get '/win' => lambda { |e| [200, {'Content-Type'=>'text/plain'}, ['WIN']] }
+ end
+ R
+
+ app 'development'
+
+ get '/win'
+ assert_equal 'WIN', last_response.body
+
+ app_file 'config/routes.rb', <<-R
+ Rails.application.routes.draw do
+ get 'lol' => 'hello#index'
+ end
+ R
+
+ get '/win'
+ assert_equal 'WIN', last_response.body
+ end
+
+ {"development" => "baz", "production" => "bar"}.each do |mode, expected|
+ test "reloads routes when configuration is changed in #{mode}" do
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def bar
+ render text: "bar"
+ end
+
+ def baz
+ render text: "baz"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#bar'
+ end
+ RUBY
+
+ app(mode)
+
+ get '/foo'
+ assert_equal 'bar', last_response.body
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#baz'
+ end
+ RUBY
+
+ sleep 0.1
+
+ get '/foo'
+ assert_equal expected, last_response.body
+ end
+ end
+
+ test 'routes are loaded just after initialization' do
+ require "#{app_path}/config/application"
+
+ # Create the rack app just inside after initialize callback
+ ActiveSupport.on_load(:after_initialize) do
+ ::InitializeRackApp = lambda { |env| [200, {}, ["InitializeRackApp"]] }
+ end
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: ::InitializeRackApp
+ end
+ RUBY
+
+ get '/foo'
+ assert_equal "InitializeRackApp", last_response.body
+ end
+
+ test 'reload_routes! is part of Rails.application API' do
+ app("development")
+ assert_nothing_raised do
+ Rails.application.reload_routes!
+ end
+ end
+
+ def test_root_path
+ app('development')
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render :text => "foo"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', :to => 'foo#index'
+ root :to => 'foo#index'
+ end
+ RUBY
+
+ remove_file 'public/index.html'
+
+ get '/'
+ assert_equal 'foo', last_response.body
+ end
+
+ test 'routes are added and removed when reloading' do
+ app('development')
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render text: "foo"
+ end
+ end
+ RUBY
+
+ controller :bar, <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render text: "bar"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+ assert_equal '/foo', Rails.application.routes.url_helpers.foo_path
+
+ get '/bar'
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal '/bar', Rails.application.routes.url_helpers.bar_path
+ end
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ get 'bar', to: 'bar#index'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+ assert_equal '/foo', Rails.application.routes.url_helpers.foo_path
+
+ get '/bar'
+ assert_equal 'bar', last_response.body
+ assert_equal '/bar', Rails.application.routes.url_helpers.bar_path
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+ assert_equal '/foo', Rails.application.routes.url_helpers.foo_path
+
+ get '/bar'
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal '/bar', Rails.application.routes.url_helpers.bar_path
+ end
+ end
+
+ test 'named routes are cleared when reloading' do
+ app('development')
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render text: "foo"
+ end
+ end
+ RUBY
+
+ controller :bar, <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render text: "bar"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':locale/foo', to: 'foo#index', as: 'foo'
+ end
+ RUBY
+
+ get '/en/foo'
+ assert_equal 'foo', last_response.body
+ assert_equal '/en/foo', Rails.application.routes.url_helpers.foo_path(:locale => 'en')
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':locale/bar', to: 'bar#index', as: 'foo'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get '/en/foo'
+ assert_equal 404, last_response.status
+
+ get '/en/bar'
+ assert_equal 'bar', last_response.body
+ assert_equal '/en/bar', Rails.application.routes.url_helpers.foo_path(:locale => 'en')
+ end
+
+ test 'resource routing with irregular inflection' do
+ app_file 'config/initializers/inflection.rb', <<-RUBY
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular 'yazi', 'yazilar'
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ resources :yazilar
+ end
+ RUBY
+
+ controller 'yazilar', <<-RUBY
+ class YazilarController < ApplicationController
+ def index
+ render text: 'yazilar#index'
+ end
+ end
+ RUBY
+
+ get '/yazilars'
+ assert_equal 404, last_response.status
+
+ get '/yazilar'
+ assert_equal 200, last_response.status
+ end
+ end
+end
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
new file mode 100644
index 0000000000..0c180339b4
--- /dev/null
+++ b/railties/test/application/runner_test.rb
@@ -0,0 +1,89 @@
+require 'isolation/abstract_unit'
+require 'env_helpers'
+
+module ApplicationTests
+ class RunnerTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include EnvHelpers
+
+ def setup
+ build_app
+ boot_rails
+
+ # Lets create a model so we have something to play with
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.count
+ 42
+ end
+ end
+ MODEL
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_should_include_runner_in_shebang_line_in_help_without_option
+ assert_match "/rails runner", Dir.chdir(app_path) { `bin/rails runner` }
+ end
+
+ def test_should_include_runner_in_shebang_line_in_help
+ assert_match "/rails runner", Dir.chdir(app_path) { `bin/rails runner --help` }
+ end
+
+ def test_should_run_ruby_statement
+ assert_match "42", Dir.chdir(app_path) { `bin/rails runner "puts User.count"` }
+ end
+
+ def test_should_run_file
+ app_file "bin/count_users.rb", <<-SCRIPT
+ puts User.count
+ SCRIPT
+
+ assert_match "42", Dir.chdir(app_path) { `bin/rails runner "bin/count_users.rb"` }
+ end
+
+ def test_should_set_dollar_0_to_file
+ app_file "bin/dollar0.rb", <<-SCRIPT
+ puts $0
+ SCRIPT
+
+ assert_match "bin/dollar0.rb", Dir.chdir(app_path) { `bin/rails runner "bin/dollar0.rb"` }
+ end
+
+ def test_should_set_dollar_program_name_to_file
+ app_file "bin/program_name.rb", <<-SCRIPT
+ puts $PROGRAM_NAME
+ SCRIPT
+
+ assert_match "bin/program_name.rb", Dir.chdir(app_path) { `bin/rails runner "bin/program_name.rb"` }
+ end
+
+ def test_with_hook
+ add_to_config <<-RUBY
+ runner do |app|
+ app.config.ran = true
+ end
+ RUBY
+
+ assert_match "true", Dir.chdir(app_path) { `bin/rails runner "puts Rails.application.config.ran"` }
+ end
+
+ def test_default_environment
+ assert_match "development", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` }
+ end
+
+ def test_environment_with_rails_env
+ with_rails_env "production" do
+ assert_match "production", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` }
+ end
+ end
+
+ def test_environment_with_rack_env
+ with_rack_env "production" do
+ assert_match "production", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` }
+ end
+ end
+ end
+end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
new file mode 100644
index 0000000000..4965ab7da0
--- /dev/null
+++ b/railties/test/application/test_runner_test.rb
@@ -0,0 +1,455 @@
+require 'isolation/abstract_unit'
+require 'active_support/core_ext/string/strip'
+require 'env_helpers'
+
+module ApplicationTests
+ class TestRunnerTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation, EnvHelpers
+
+ def setup
+ build_app
+ create_schema
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def test_run_single_file
+ create_test_file :models, 'foo'
+ create_test_file :models, 'bar'
+ assert_match "1 runs, 1 assertions, 0 failures", run_test_command("test/models/foo_test.rb")
+ end
+
+ def test_run_multiple_files
+ create_test_file :models, 'foo'
+ create_test_file :models, 'bar'
+ assert_match "2 runs, 2 assertions, 0 failures", run_test_command("test/models/foo_test.rb test/models/bar_test.rb")
+ end
+
+ def test_run_file_with_syntax_error
+ app_file 'test/models/error_test.rb', <<-RUBY
+ require 'test_helper'
+ def; end
+ RUBY
+
+ error = capture(:stderr) { run_test_command('test/models/error_test.rb') }
+ assert_match "syntax error", error
+ end
+
+ def test_run_models
+ create_test_file :models, 'foo'
+ create_test_file :models, 'bar'
+ create_test_file :controllers, 'foobar_controller'
+ run_test_command("test/models").tap do |output|
+ assert_match "FooTest", output
+ assert_match "BarTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_helpers
+ create_test_file :helpers, 'foo_helper'
+ create_test_file :helpers, 'bar_helper'
+ create_test_file :controllers, 'foobar_controller'
+ run_test_command("test/helpers").tap do |output|
+ assert_match "FooHelperTest", output
+ assert_match "BarHelperTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_units
+ skip "we no longer have the concept of unit tests. Just different directories..."
+ create_test_file :models, 'foo'
+ create_test_file :helpers, 'bar_helper'
+ create_test_file :unit, 'baz_unit'
+ create_test_file :controllers, 'foobar_controller'
+ run_test_units_command.tap do |output|
+ assert_match "FooTest", output
+ assert_match "BarHelperTest", output
+ assert_match "BazUnitTest", output
+ assert_match "3 runs, 3 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_controllers
+ create_test_file :controllers, 'foo_controller'
+ create_test_file :controllers, 'bar_controller'
+ create_test_file :models, 'foo'
+ run_test_command("test/controllers").tap do |output|
+ assert_match "FooControllerTest", output
+ assert_match "BarControllerTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_mailers
+ create_test_file :mailers, 'foo_mailer'
+ create_test_file :mailers, 'bar_mailer'
+ create_test_file :models, 'foo'
+ run_test_command("test/mailers").tap do |output|
+ assert_match "FooMailerTest", output
+ assert_match "BarMailerTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_jobs
+ create_test_file :jobs, 'foo_job'
+ create_test_file :jobs, 'bar_job'
+ create_test_file :models, 'foo'
+ run_test_command("test/jobs").tap do |output|
+ assert_match "FooJobTest", output
+ assert_match "BarJobTest", output
+ assert_match "2 runs, 2 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_functionals
+ skip "we no longer have the concept of functional tests. Just different directories..."
+ create_test_file :mailers, 'foo_mailer'
+ create_test_file :controllers, 'bar_controller'
+ create_test_file :functional, 'baz_functional'
+ create_test_file :models, 'foo'
+ run_test_functionals_command.tap do |output|
+ assert_match "FooMailerTest", output
+ assert_match "BarControllerTest", output
+ assert_match "BazFunctionalTest", output
+ assert_match "3 runs, 3 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_integration
+ create_test_file :integration, 'foo_integration'
+ create_test_file :models, 'foo'
+ run_test_command("test/integration").tap do |output|
+ assert_match "FooIntegration", output
+ assert_match "1 runs, 1 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_all_suites
+ suites = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration, :jobs]
+ suites.each { |suite| create_test_file suite, "foo_#{suite}" }
+ run_test_command('') .tap do |output|
+ suites.each { |suite| assert_match "Foo#{suite.to_s.camelize}Test", output }
+ assert_match "8 runs, 8 assertions, 0 failures", output
+ end
+ end
+
+ def test_run_named_test
+ app_file 'test/unit/chu_2_koi_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class Chu2KoiTest < ActiveSupport::TestCase
+ def test_rikka
+ puts 'Rikka'
+ end
+
+ def test_sanae
+ puts 'Sanae'
+ end
+ end
+ RUBY
+
+ run_test_command('-n test_rikka test/unit/chu_2_koi_test.rb').tap do |output|
+ assert_match "Rikka", output
+ assert_no_match "Sanae", output
+ end
+ end
+
+ def test_run_matched_test
+ app_file 'test/unit/chu_2_koi_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class Chu2KoiTest < ActiveSupport::TestCase
+ def test_rikka
+ puts 'Rikka'
+ end
+
+ def test_sanae
+ puts 'Sanae'
+ end
+ end
+ RUBY
+
+ run_test_command('-n /rikka/ test/unit/chu_2_koi_test.rb').tap do |output|
+ assert_match "Rikka", output
+ assert_no_match "Sanae", output
+ end
+ end
+
+ def test_load_fixtures_when_running_test_suites
+ create_model_with_fixture
+ suites = [:models, :helpers, :controllers, :mailers, :integration]
+
+ suites.each do |suite, directory|
+ directory ||= suite
+ create_fixture_test directory
+ assert_match "3 users", run_test_command("test/#{suite}")
+ Dir.chdir(app_path) { FileUtils.rm_f "test/#{directory}" }
+ end
+ end
+
+ def test_run_with_model
+ skip "These feel a bit odd. Not sure we should keep supporting them."
+ create_model_with_fixture
+ create_fixture_test 'models', 'user'
+ assert_match "3 users", run_task(["test models/user"])
+ assert_match "3 users", run_task(["test app/models/user.rb"])
+ end
+
+ def test_run_different_environment_using_env_var
+ skip "no longer possible. Running tests in a different environment should be explicit"
+ app_file 'test/unit/env_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class EnvTest < ActiveSupport::TestCase
+ def test_env
+ puts Rails.env
+ end
+ end
+ RUBY
+
+ ENV['RAILS_ENV'] = 'development'
+ assert_match "development", run_test_command('test/unit/env_test.rb')
+ end
+
+ def test_run_in_test_environment_by_default
+ create_env_test
+
+ assert_match "Current Environment: test", run_test_command('test/unit/env_test.rb')
+ end
+
+ def test_run_different_environment
+ create_env_test
+
+ assert_match "Current Environment: development",
+ run_test_command("-e development test/unit/env_test.rb")
+ end
+
+ def test_generated_scaffold_works_with_rails_test
+ create_scaffold
+ assert_match "0 failures, 0 errors, 0 skips", run_test_command('')
+ end
+
+ def test_run_multiple_folders
+ create_test_file :models, 'account'
+ create_test_file :controllers, 'accounts_controller'
+
+ run_test_command('test/models test/controllers').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'AccountsControllerTest', output
+ assert_match '2 runs, 2 assertions, 0 failures, 0 errors, 0 skips', output
+ end
+ end
+
+ def test_run_with_ruby_command
+ app_file 'test/models/post_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ test 'declarative syntax works' do
+ puts 'PostTest'
+ assert true
+ end
+ end
+ RUBY
+
+ Dir.chdir(app_path) do
+ `ruby -Itest test/models/post_test.rb`.tap do |output|
+ assert_match 'PostTest', output
+ assert_no_match 'is already defined in', output
+ end
+ end
+ end
+
+ def test_mix_files_and_line_filters
+ create_test_file :models, 'account'
+ app_file 'test/models/post_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ def test_post
+ puts 'PostTest'
+ assert true
+ end
+
+ def test_line_filter_does_not_run_this
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command('test/models/account_test.rb test/models/post_test.rb:4').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'PostTest', output
+ assert_match '2 runs, 2 assertions', output
+ end
+ end
+
+ def test_multiple_line_filters
+ create_test_file :models, 'account'
+ create_test_file :models, 'post'
+
+ run_test_command('test/models/account_test.rb:4 test/models/post_test.rb:4').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'PostTest', output
+ end
+ end
+
+ def test_line_filter_without_line_runs_all_tests
+ create_test_file :models, 'account'
+
+ run_test_command('test/models/account_test.rb:').tap do |output|
+ assert_match 'AccountTest', output
+ end
+ end
+
+ def test_shows_filtered_backtrace_by_default
+ create_backtrace_test
+
+ assert_match 'Rails::BacktraceCleaner', run_test_command('test/unit/backtrace_test.rb')
+ end
+
+ def test_backtrace_option
+ create_backtrace_test
+
+ assert_match 'Minitest::BacktraceFilter', run_test_command('test/unit/backtrace_test.rb -b')
+ assert_match 'Minitest::BacktraceFilter',
+ run_test_command('test/unit/backtrace_test.rb --backtrace')
+ end
+
+ def test_show_full_backtrace_using_backtrace_environment_variable
+ create_backtrace_test
+
+ switch_env 'BACKTRACE', 'true' do
+ assert_match 'Minitest::BacktraceFilter', run_test_command('test/unit/backtrace_test.rb')
+ end
+ end
+
+ def test_run_app_without_rails_loaded
+ # Simulate a real Rails app boot.
+ app_file 'config/boot.rb', <<-RUBY
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+
+ require 'bundler/setup' # Set up gems listed in the Gemfile.
+ RUBY
+
+ assert_match '0 runs, 0 assertions', run_test_command('')
+ end
+
+ def test_output_inline_by_default
+ create_test_file :models, 'post', pass: false
+
+ output = run_test_command('test/models/post_test.rb')
+ assert_match %r{Running:\n\nPostTest\nF\n\nwups!\n\nbin/rails test test/models/post_test.rb:6}, output
+ end
+
+ def test_only_inline_failure_output
+ create_test_file :models, 'post', pass: false
+
+ output = run_test_command('test/models/post_test.rb')
+ assert_match %r{Finished in.*\n\n1 runs, 1 assertions}, output
+ end
+
+ def test_fail_fast
+ create_test_file :models, 'post', pass: false
+
+ assert_match(/Interrupt/,
+ capture(:stderr) { run_test_command('test/models/post_test.rb --fail-fast') })
+ end
+
+ def test_raise_error_when_specified_file_does_not_exist
+ error = capture(:stderr) { run_test_command('test/not_exists.rb') }
+ assert_match(%r{cannot load such file.+test/not_exists\.rb}, error)
+ end
+
+ private
+ def run_test_command(arguments = 'test/unit/test_test.rb')
+ Dir.chdir(app_path) { `bin/rails t #{arguments}` }
+ end
+
+ def create_model_with_fixture
+ script 'generate model user name:string'
+
+ app_file 'test/fixtures/users.yml', <<-YAML.strip_heredoc
+ vampire:
+ id: 1
+ name: Koyomi Araragi
+ crab:
+ id: 2
+ name: Senjougahara Hitagi
+ cat:
+ id: 3
+ name: Tsubasa Hanekawa
+ YAML
+
+ run_migration
+ end
+
+ def create_fixture_test(path = :unit, name = 'test')
+ app_file "test/#{path}/#{name}_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class #{name.camelize}Test < ActiveSupport::TestCase
+ def test_fixture
+ puts "\#{User.count} users (\#{__FILE__})"
+ end
+ end
+ RUBY
+ end
+
+ def create_backtrace_test
+ app_file 'test/unit/backtrace_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class BacktraceTest < ActiveSupport::TestCase
+ def test_backtrace
+ puts Minitest.backtrace_filter
+ end
+ end
+ RUBY
+ end
+
+ def create_schema
+ app_file 'db/schema.rb', ''
+ end
+
+ def create_test_file(path = :unit, name = 'test', pass: true)
+ app_file "test/#{path}/#{name}_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class #{name.camelize}Test < ActiveSupport::TestCase
+ def test_truth
+ puts "#{name.camelize}Test"
+ assert #{pass}, 'wups!'
+ end
+ end
+ RUBY
+ end
+
+ def create_env_test
+ app_file 'test/unit/env_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class EnvTest < ActiveSupport::TestCase
+ def test_env
+ puts "Current Environment: \#{Rails.env}"
+ end
+ end
+ RUBY
+ end
+
+ def create_scaffold
+ script 'generate scaffold user name:string'
+ Dir.chdir(app_path) { File.exist?('app/models/user.rb') }
+ run_migration
+ end
+
+ def run_migration
+ Dir.chdir(app_path) { `bin/rake db:migrate` }
+ end
+ end
+end
diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb
new file mode 100644
index 0000000000..0e997f4ba7
--- /dev/null
+++ b/railties/test/application/test_test.rb
@@ -0,0 +1,307 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class TestTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "truth" do
+ app_file 'test/unit/foo_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class FooTest < ActiveSupport::TestCase
+ def test_truth
+ assert true
+ end
+ end
+ RUBY
+
+ assert_successful_test_run 'unit/foo_test.rb'
+ end
+
+ test "integration test" do
+ controller 'posts', <<-RUBY
+ class PostsController < ActionController::Base
+ end
+ RUBY
+
+ app_file 'app/views/posts/index.html.erb', <<-HTML
+ Posts#index
+ HTML
+
+ app_file 'test/integration/posts_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class PostsTest < ActionDispatch::IntegrationTest
+ def test_index
+ get '/posts'
+ assert_response :success
+ assert_includes @response.body, 'Posts#index'
+ end
+ end
+ RUBY
+
+ assert_successful_test_run 'integration/posts_test.rb'
+ end
+
+ test "enable full backtraces on test failures" do
+ app_file 'test/unit/failing_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class FailingTest < ActiveSupport::TestCase
+ def test_failure
+ raise "fail"
+ end
+ end
+ RUBY
+
+ output = run_test_file('unit/failing_test.rb', env: { "BACKTRACE" => "1" })
+ assert_match %r{test/unit/failing_test\.rb}, output
+ assert_match %r{test/unit/failing_test\.rb:4}, output
+ end
+
+ test "ruby schema migrations" do
+ output = script('generate model user name:string')
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file 'test/models/user_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+ app_file 'db/schema.rb', ''
+
+ assert_unsuccessful_run "models/user_test.rb", "Migrations are pending"
+
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ app_file 'config/initializers/disable_maintain_test_schema.rb', <<-RUBY
+ Rails.application.config.active_record.maintain_test_schema = false
+ RUBY
+
+ assert_unsuccessful_run "models/user_test.rb", "Could not find table 'users'"
+
+ File.delete "#{app_path}/config/initializers/disable_maintain_test_schema.rb"
+
+ result = assert_successful_test_run('models/user_test.rb')
+ assert !result.include?("create_table(:users)")
+ end
+
+ test "sql structure migrations" do
+ output = script('generate model user name:string')
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file 'test/models/user_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+
+ app_file 'db/structure.sql', ''
+ app_file 'config/initializers/enable_sql_schema_format.rb', <<-RUBY
+ Rails.application.config.active_record.schema_format = :sql
+ RUBY
+
+ assert_unsuccessful_run "models/user_test.rb", "Migrations are pending"
+
+ app_file 'db/structure.sql', <<-SQL
+ CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
+ CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
+ INSERT INTO schema_migrations (version) VALUES ('#{version}');
+ SQL
+
+ app_file 'config/initializers/disable_maintain_test_schema.rb', <<-RUBY
+ Rails.application.config.active_record.maintain_test_schema = false
+ RUBY
+
+ assert_unsuccessful_run "models/user_test.rb", "Could not find table 'users'"
+
+ File.delete "#{app_path}/config/initializers/disable_maintain_test_schema.rb"
+
+ assert_successful_test_run('models/user_test.rb')
+ end
+
+ test "sql structure migrations when adding column to existing table" do
+ output_1 = script('generate model user name:string')
+ version_1 = output_1.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file 'test/models/user_test.rb', <<-RUBY
+ require 'test_helper'
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+
+ app_file 'config/initializers/enable_sql_schema_format.rb', <<-RUBY
+ Rails.application.config.active_record.schema_format = :sql
+ RUBY
+
+ app_file 'db/structure.sql', <<-SQL
+ CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
+ CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
+ INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
+ SQL
+
+ assert_successful_test_run('models/user_test.rb')
+
+ output_2 = script('generate migration add_email_to_users')
+ version_2 = output_2.match(/(\d+)_add_email_to_users\.rb/)[1]
+
+ app_file 'test/models/user_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon", email: "jon@doe.com"
+ end
+ end
+ RUBY
+
+ app_file 'db/structure.sql', <<-SQL
+ CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
+ CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
+ CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "email" varchar(255));
+ INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
+ INSERT INTO schema_migrations (version) VALUES ('#{version_2}');
+ SQL
+
+ assert_successful_test_run('models/user_test.rb')
+ end
+
+ # TODO: would be nice if we could detect the schema change automatically.
+ # For now, the user has to synchronize the schema manually.
+ # This test-case serves as a reminder for this use-case.
+ test "manually synchronize test schema after rollback" do
+ output = script('generate model user name:string')
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file 'test/models/user_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ assert_equal ["id", "name"], User.columns_hash.keys
+ end
+ end
+ RUBY
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ assert_successful_test_run "models/user_test.rb"
+
+ # Simulate `db:rollback` + edit of the migration file + `db:migrate`
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ t.integer :age
+ end
+ end
+ RUBY
+
+ assert_successful_test_run "models/user_test.rb"
+
+ Dir.chdir(app_path) { `bin/rake db:test:prepare` }
+
+ assert_unsuccessful_run "models/user_test.rb", <<-ASSERTION
+Expected: ["id", "name"]
+ Actual: ["id", "name", "age"]
+ ASSERTION
+ end
+
+ test "hooks for plugins" do
+ output = script('generate model user name:string')
+ version = output.match(/(\d+)_create_users\.rb/)[1]
+
+ app_file 'lib/tasks/hooks.rake', <<-RUBY
+ task :before_hook do
+ has_user_table = ActiveRecord::Base.connection.table_exists?('users')
+ puts "before: " + has_user_table.to_s
+ end
+
+ task :after_hook do
+ has_user_table = ActiveRecord::Base.connection.table_exists?('users')
+ puts "after: " + has_user_table.to_s
+ end
+
+ Rake::Task["db:test:prepare"].enhance [:before_hook] do
+ Rake::Task[:after_hook].invoke
+ end
+ RUBY
+ app_file 'test/models/user_test.rb', <<-RUBY
+ require 'test_helper'
+ class UserTest < ActiveSupport::TestCase
+ test "user" do
+ User.create! name: "Jon"
+ end
+ end
+ RUBY
+
+ # Simulate `db:migrate`
+ app_file 'db/schema.rb', <<-RUBY
+ ActiveRecord::Schema.define(version: #{version}) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
+ output = assert_successful_test_run "models/user_test.rb"
+ assert_includes output, "before: false\nafter: true"
+
+ # running tests again won't trigger a schema update
+ output = assert_successful_test_run "models/user_test.rb"
+ assert_not_includes output, "before:"
+ assert_not_includes output, "after:"
+ end
+
+ private
+ def assert_unsuccessful_run(name, message)
+ result = run_test_file(name)
+ assert_not_equal 0, $?.to_i
+ assert result.include?(message)
+ result
+ end
+
+ def assert_successful_test_run(name)
+ result = run_test_file(name)
+ assert_equal 0, $?.to_i, result
+ result
+ end
+
+ def run_test_file(name, options = {})
+ Dir.chdir(app_path) { `bin/rails test "#{app_path}/test/#{name}" 2>&1` }
+ end
+ end
+end
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
new file mode 100644
index 0000000000..894e18cb39
--- /dev/null
+++ b/railties/test/application/url_generation_test.rb
@@ -0,0 +1,59 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class UrlGenerationTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def app
+ Rails.application
+ end
+
+ test "it works" do
+ boot_rails
+ require "rails"
+ require "action_controller/railtie"
+ require "action_view/railtie"
+
+ class MyApp < Rails::Application
+ secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4"
+ config.session_store :cookie_store, key: "_myapp_session"
+ config.active_support.deprecation = :log
+ config.eager_load = false
+ end
+
+ Rails.application.initialize!
+
+ class ::ApplicationController < ActionController::Base
+ end
+
+ class ::OmgController < ::ApplicationController
+ def index
+ render text: omg_path
+ end
+ end
+
+ MyApp.routes.draw do
+ get "/" => "omg#index", as: :omg
+ end
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/"
+ assert_equal "/", last_response.body
+ end
+
+ def test_routes_know_the_relative_root
+ boot_rails
+ require "rails"
+ require "action_controller/railtie"
+ require "action_view/railtie"
+
+ relative_url = '/hello'
+ ENV["RAILS_RELATIVE_URL_ROOT"] = relative_url
+ app = Class.new(Rails::Application)
+ assert_equal relative_url, app.routes.relative_url_root
+ ENV["RAILS_RELATIVE_URL_ROOT"] = nil
+ end
+ end
+end
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
new file mode 100644
index 0000000000..2dd74f8fd1
--- /dev/null
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -0,0 +1,24 @@
+require 'abstract_unit'
+require 'rails/backtrace_cleaner'
+
+class BacktraceCleanerVendorGemTest < ActiveSupport::TestCase
+ def setup
+ @cleaner = Rails::BacktraceCleaner.new
+ end
+
+ test "should format installed gems correctly" do
+ @backtrace = [ "#{Gem.path[0]}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
+ @result = @cleaner.clean(@backtrace, :all)
+ assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0]
+ end
+
+ test "should format installed gems not in Gem.default_dir correctly" do
+ @target_dir = Gem.path.detect { |p| p != Gem.default_dir }
+ # skip this test if default_dir is the only directory on Gem.path
+ if @target_dir
+ @backtrace = [ "#{@target_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
+ @result = @cleaner.clean(@backtrace, :all)
+ assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0]
+ end
+ end
+end
diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb
new file mode 100644
index 0000000000..cecc3908b3
--- /dev/null
+++ b/railties/test/code_statistics_calculator_test.rb
@@ -0,0 +1,330 @@
+require 'abstract_unit'
+require 'rails/code_statistics_calculator'
+
+class CodeStatisticsCalculatorTest < ActiveSupport::TestCase
+ def setup
+ @code_statistics_calculator = CodeStatisticsCalculator.new
+ end
+
+ test 'calculate statistics using #add_by_file_path' do
+ code = <<-RUBY
+ def foo
+ puts 'foo'
+ # bar
+ end
+ RUBY
+
+ temp_file 'stats.rb', code do |path|
+ @code_statistics_calculator.add_by_file_path path
+
+ assert_equal 4, @code_statistics_calculator.lines
+ assert_equal 3, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 1, @code_statistics_calculator.methods
+ end
+ end
+
+ test 'count number of methods in MiniTest file' do
+ code = <<-RUBY
+ class FooTest < ActionController::TestCase
+ test 'expectation' do
+ assert true
+ end
+
+ def test_expectation
+ assert true
+ end
+ end
+ RUBY
+
+ temp_file 'foo_test.rb', code do |path|
+ @code_statistics_calculator.add_by_file_path path
+ assert_equal 2, @code_statistics_calculator.methods
+ end
+ end
+
+ test 'add statistics to another using #add' do
+ code_statistics_calculator_1 = CodeStatisticsCalculator.new(1, 2, 3, 4)
+ @code_statistics_calculator.add(code_statistics_calculator_1)
+
+ assert_equal 1, @code_statistics_calculator.lines
+ assert_equal 2, @code_statistics_calculator.code_lines
+ assert_equal 3, @code_statistics_calculator.classes
+ assert_equal 4, @code_statistics_calculator.methods
+
+ code_statistics_calculator_2 = CodeStatisticsCalculator.new(2, 3, 4, 5)
+ @code_statistics_calculator.add(code_statistics_calculator_2)
+
+ assert_equal 3, @code_statistics_calculator.lines
+ assert_equal 5, @code_statistics_calculator.code_lines
+ assert_equal 7, @code_statistics_calculator.classes
+ assert_equal 9, @code_statistics_calculator.methods
+ end
+
+ test 'accumulate statistics using #add_by_io' do
+ code_statistics_calculator_1 = CodeStatisticsCalculator.new(1, 2, 3, 4)
+ @code_statistics_calculator.add(code_statistics_calculator_1)
+
+ code = <<-'CODE'
+ def foo
+ puts 'foo'
+ end
+
+ def bar; end
+ class A; end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 7, @code_statistics_calculator.lines
+ assert_equal 7, @code_statistics_calculator.code_lines
+ assert_equal 4, @code_statistics_calculator.classes
+ assert_equal 6, @code_statistics_calculator.methods
+ end
+
+ test 'calculate number of Ruby methods' do
+ code = <<-'CODE'
+ def foo
+ puts 'foo'
+ end
+
+ def bar; end
+
+ class Foo
+ def bar(abc)
+ end
+ end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 3, @code_statistics_calculator.methods
+ end
+
+ test 'calculate Ruby LOCs' do
+ code = <<-'CODE'
+ def foo
+ puts 'foo'
+ end
+
+ # def bar; end
+
+ class A < B
+ end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 8, @code_statistics_calculator.lines
+ assert_equal 5, @code_statistics_calculator.code_lines
+ end
+
+ test 'calculate number of Ruby classes' do
+ code = <<-'CODE'
+ class Foo < Bar
+ def foo
+ puts 'foo'
+ end
+ end
+
+ class Z; end
+
+ # class A
+ # end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 2, @code_statistics_calculator.classes
+ end
+
+ test 'skip Ruby comments' do
+ code = <<-'CODE'
+=begin
+ class Foo
+ def foo
+ puts 'foo'
+ end
+ end
+=end
+
+ # class A
+ # end
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rb)
+
+ assert_equal 10, @code_statistics_calculator.lines
+ assert_equal 0, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 0, @code_statistics_calculator.methods
+ end
+
+ test 'calculate number of JS methods' do
+ code = <<-'CODE'
+ function foo(x, y, z) {
+ doX();
+ }
+
+ $(function () {
+ bar();
+ })
+
+ var baz = function ( x ) {
+ }
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :js)
+
+ assert_equal 3, @code_statistics_calculator.methods
+ end
+
+ test 'calculate JS LOCs' do
+ code = <<-'CODE'
+ function foo()
+ alert('foo');
+ end
+
+ // var b = 2;
+
+ var a = 1;
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :js)
+
+ assert_equal 7, @code_statistics_calculator.lines
+ assert_equal 4, @code_statistics_calculator.code_lines
+ end
+
+ test 'skip JS comments' do
+ code = <<-'CODE'
+ /*
+ * var f = function () {
+ 1 / 2;
+ }
+ */
+
+ // call();
+ //
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :js)
+
+ assert_equal 8, @code_statistics_calculator.lines
+ assert_equal 0, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 0, @code_statistics_calculator.methods
+ end
+
+ test 'calculate number of CoffeeScript methods' do
+ code = <<-'CODE'
+ square = (x) -> x * x
+
+ math =
+ cube: (x) -> x * square x
+
+ fill = (container, liquid = "coffee") ->
+ "Filling the #{container} with #{liquid}..."
+
+ $('.shopping_cart').bind 'click', (event) =>
+ @customer.purchase @cart
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 4, @code_statistics_calculator.methods
+ end
+
+ test 'calculate CoffeeScript LOCs' do
+ code = <<-'CODE'
+ # Assignment:
+ number = 42
+ opposite = true
+
+ ###
+ CoffeeScript Compiler v1.4.0
+ Released under the MIT License
+ ###
+
+ # Conditions:
+ number = -42 if opposite
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 11, @code_statistics_calculator.lines
+ assert_equal 3, @code_statistics_calculator.code_lines
+ end
+
+ test 'calculate number of CoffeeScript classes' do
+ code = <<-'CODE'
+ class Animal
+ constructor: (@name) ->
+
+ move: (meters) ->
+ alert @name + " moved #{meters}m."
+
+ class Snake extends Animal
+ move: ->
+ alert "Slithering..."
+ super 5
+
+ # class Horse
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 2, @code_statistics_calculator.classes
+ end
+
+ test 'skip CoffeeScript comments' do
+ code = <<-'CODE'
+###
+class Animal
+ constructor: (@name) ->
+
+ move: (meters) ->
+ alert @name + " moved #{meters}m."
+ ###
+
+ # class Horse
+ alert 'hello'
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :coffee)
+
+ assert_equal 10, @code_statistics_calculator.lines
+ assert_equal 1, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 0, @code_statistics_calculator.methods
+ end
+
+ test 'count rake tasks' do
+ code = <<-'CODE'
+ task :test_task do
+ puts 'foo'
+ end
+
+ CODE
+
+ @code_statistics_calculator.add_by_io(StringIO.new(code), :rake)
+
+ assert_equal 4, @code_statistics_calculator.lines
+ assert_equal 3, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 0, @code_statistics_calculator.methods
+ end
+
+ private
+ def temp_file(name, content)
+ dir = File.expand_path '../fixtures/tmp', __FILE__
+ path = "#{dir}/#{name}"
+
+ FileUtils.mkdir_p dir
+ File.write path, content
+
+ yield path
+ ensure
+ FileUtils.rm_rf path
+ end
+end
diff --git a/railties/test/code_statistics_test.rb b/railties/test/code_statistics_test.rb
new file mode 100644
index 0000000000..1b1ff80bc1
--- /dev/null
+++ b/railties/test/code_statistics_test.rb
@@ -0,0 +1,20 @@
+require 'abstract_unit'
+require 'rails/code_statistics'
+
+class CodeStatisticsTest < ActiveSupport::TestCase
+ def setup
+ @tmp_path = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'tmp'))
+ @dir_js = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'tmp', 'lib.js'))
+ FileUtils.mkdir_p(@dir_js)
+ end
+
+ def teardown
+ FileUtils.rm_rf(@tmp_path)
+ end
+
+ test 'ignores directories that happen to have source files extensions' do
+ assert_nothing_raised do
+ @code_statistics = CodeStatistics.new(['tmp dir', @tmp_path])
+ end
+ end
+end
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
new file mode 100644
index 0000000000..de0cf0ba9e
--- /dev/null
+++ b/railties/test/commands/console_test.rb
@@ -0,0 +1,154 @@
+require 'abstract_unit'
+require 'env_helpers'
+require 'rails/commands/console'
+
+class Rails::ConsoleTest < ActiveSupport::TestCase
+ include EnvHelpers
+
+ class FakeConsole
+ def self.started?
+ @started
+ end
+
+ def self.start
+ @started = true
+ end
+ end
+
+ def test_sandbox_option
+ console = Rails::Console.new(app, parse_arguments(["--sandbox"]))
+ assert console.sandbox?
+ end
+
+ def test_short_version_of_sandbox_option
+ console = Rails::Console.new(app, parse_arguments(["-s"]))
+ assert console.sandbox?
+ end
+
+ def test_no_options
+ console = Rails::Console.new(app, parse_arguments([]))
+ assert !console.sandbox?
+ end
+
+ def test_start
+ start
+
+ assert app.console.started?
+ assert_match(/Loading \w+ environment \(Rails/, output)
+ end
+
+ def test_start_with_sandbox
+ start ["--sandbox"]
+
+
+ assert app.console.started?
+ assert app.sandbox
+ assert_match(/Loading \w+ environment in sandbox \(Rails/, output)
+ end
+
+ def test_console_with_environment
+ start ["-e production"]
+ assert_match(/\sproduction\s/, output)
+ end
+
+ def test_console_defaults_to_IRB
+ app = build_app(nil)
+ assert_equal IRB, Rails::Console.new(app).console
+ end
+
+ def test_default_environment_with_no_rails_env
+ with_rails_env nil do
+ start
+ assert_match(/\sdevelopment\s/, output)
+ end
+ end
+
+ def test_default_environment_with_rails_env
+ with_rails_env 'special-production' do
+ start
+ assert_match(/\sspecial-production\s/, output)
+ end
+ end
+
+ def test_default_environment_with_rack_env
+ with_rack_env 'production' do
+ start
+ assert_match(/\sproduction\s/, output)
+ end
+ end
+
+ def test_e_option
+ start ['-e', 'special-production']
+ assert_match(/\sspecial-production\s/, output)
+ end
+
+ def test_environment_option
+ start ['--environment=special-production']
+ assert_match(/\sspecial-production\s/, output)
+ end
+
+ def test_rails_env_is_production_when_first_argument_is_p
+ start ['p']
+ assert_match(/\sproduction\s/, output)
+ end
+
+ def test_rails_env_is_test_when_first_argument_is_t
+ start ['t']
+ assert_match(/\stest\s/, output)
+ end
+
+ def test_rails_env_is_development_when_argument_is_d
+ start ['d']
+ assert_match(/\sdevelopment\s/, output)
+ end
+
+ def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
+ stubbed_console = Class.new(Rails::Console) do
+ def available_environments
+ ['dev']
+ end
+ end
+ options = stubbed_console.parse_arguments(['dev'])
+ assert_match('dev', options[:environment])
+ end
+
+ attr_reader :output
+ private :output
+
+ private
+
+ def start(argv = [])
+ rails_console = Rails::Console.new(app, parse_arguments(argv))
+ @output = capture(:stdout) { rails_console.start }
+ end
+
+ def app
+ @app ||= build_app(FakeConsole)
+ end
+
+ def build_app(console)
+ mocked_console = Class.new do
+ attr_reader :sandbox, :console
+
+ def initialize(console)
+ @console = console
+ end
+
+ def config
+ self
+ end
+
+ def sandbox=(arg)
+ @sandbox = arg
+ end
+
+ def load_console
+ end
+ end
+ mocked_console.new(console)
+ end
+
+ def parse_arguments(args)
+ Rails::Console.parse_arguments(args)
+ end
+end
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
new file mode 100644
index 0000000000..7950ed6aa7
--- /dev/null
+++ b/railties/test/commands/dbconsole_test.rb
@@ -0,0 +1,264 @@
+require 'abstract_unit'
+require 'minitest/mock'
+require 'rails/commands/dbconsole'
+
+class Rails::DBConsoleTest < ActiveSupport::TestCase
+
+
+ def setup
+ Rails::DBConsole.const_set('APP_PATH', 'rails/all')
+ end
+
+ def teardown
+ Rails::DBConsole.send(:remove_const, 'APP_PATH')
+ %w[PGUSER PGHOST PGPORT PGPASSWORD DATABASE_URL].each{|key| ENV.delete(key)}
+ end
+
+ def test_config_with_db_config_only
+ config_sample = {
+ "test"=> {
+ "adapter"=> "sqlite3",
+ "host"=> "localhost",
+ "port"=> "9000",
+ "database"=> "foo_test",
+ "user"=> "foo",
+ "password"=> "bar",
+ "pool"=> "5",
+ "timeout"=> "3000"
+ }
+ }
+ app_db_config(config_sample) do
+ assert_equal config_sample["test"], Rails::DBConsole.new.config
+ end
+ end
+
+ def test_config_with_no_db_config
+ app_db_config(nil) do
+ assert_raise(ActiveRecord::AdapterNotSpecified) {
+ Rails::DBConsole.new.config
+ }
+ end
+ end
+
+ def test_config_with_database_url_only
+ ENV['DATABASE_URL'] = 'postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000'
+ expected = {
+ "adapter" => "postgresql",
+ "host" => "localhost",
+ "port" => 9000,
+ "database" => "foo_test",
+ "username" => "foo",
+ "password" => "bar",
+ "pool" => "5",
+ "timeout" => "3000"
+ }.sort
+
+ app_db_config(nil) do
+ assert_equal expected, Rails::DBConsole.new.config.sort
+ end
+ end
+
+ def test_config_choose_database_url_if_exists
+ host = "database-url-host.com"
+ ENV['DATABASE_URL'] = "postgresql://foo:bar@#{host}:9000/foo_test?pool=5&timeout=3000"
+ sample_config = {
+ "test" => {
+ "adapter" => "postgresql",
+ "host" => "not-the-#{host}",
+ "port" => 9000,
+ "database" => "foo_test",
+ "username" => "foo",
+ "password" => "bar",
+ "pool" => "5",
+ "timeout" => "3000"
+ }
+ }
+ app_db_config(sample_config) do
+ assert_equal host, Rails::DBConsole.new.config["host"]
+ end
+ end
+
+ def test_env
+ assert_equal "test", Rails::DBConsole.new.environment
+
+ ENV['RAILS_ENV'] = nil
+ ENV['RACK_ENV'] = nil
+
+ Rails.stub(:respond_to?, false) do
+ assert_equal "development", Rails::DBConsole.new.environment
+
+ ENV['RACK_ENV'] = "rack_env"
+ assert_equal "rack_env", Rails::DBConsole.new.environment
+
+ ENV['RAILS_ENV'] = "rails_env"
+ assert_equal "rails_env", Rails::DBConsole.new.environment
+ end
+ ensure
+ ENV['RAILS_ENV'] = "test"
+ ENV['RACK_ENV'] = nil
+ end
+
+ def test_rails_env_is_development_when_argument_is_dev
+ Rails::DBConsole.stub(:available_environments, ['development', 'test']) do
+ options = Rails::DBConsole.send(:parse_arguments, ['dev'])
+ assert_match('development', options[:environment])
+ end
+ end
+
+ def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
+ Rails::DBConsole.stub(:available_environments, ['dev']) do
+ options = Rails::DBConsole.send(:parse_arguments, ['dev'])
+ assert_match('dev', options[:environment])
+ end
+ end
+
+ def test_mysql
+ start(adapter: 'mysql', database: 'db')
+ assert !aborted
+ assert_equal [%w[mysql mysql5], 'db'], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_mysql_full
+ start(adapter: 'mysql', database: 'db', host: 'locahost', port: 1234, socket: 'socket', username: 'user', password: 'qwerty', encoding: 'UTF-8')
+ assert !aborted
+ assert_equal [%w[mysql mysql5], '--host=locahost', '--port=1234', '--socket=socket', '--user=user', '--default-character-set=UTF-8', '-p', 'db'], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_mysql_include_password
+ start({adapter: 'mysql', database: 'db', username: 'user', password: 'qwerty'}, ['-p'])
+ assert !aborted
+ assert_equal [%w[mysql mysql5], '--user=user', '--password=qwerty', 'db'], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_postgresql
+ start(adapter: 'postgresql', database: 'db')
+ assert !aborted
+ assert_equal ['psql', 'db'], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_postgresql_full
+ start(adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3', host: 'host', port: 5432)
+ assert !aborted
+ assert_equal ['psql', 'db'], dbconsole.find_cmd_and_exec_args
+ assert_equal 'user', ENV['PGUSER']
+ assert_equal 'host', ENV['PGHOST']
+ assert_equal '5432', ENV['PGPORT']
+ assert_not_equal 'q1w2e3', ENV['PGPASSWORD']
+ end
+
+ def test_postgresql_include_password
+ start({adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3'}, ['-p'])
+ assert !aborted
+ assert_equal ['psql', 'db'], dbconsole.find_cmd_and_exec_args
+ assert_equal 'user', ENV['PGUSER']
+ assert_equal 'q1w2e3', ENV['PGPASSWORD']
+ end
+
+ def test_sqlite3
+ start(adapter: 'sqlite3', database: 'db.sqlite3')
+ assert !aborted
+ assert_equal ['sqlite3', Rails.root.join('db.sqlite3').to_s], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_sqlite3_mode
+ start({adapter: 'sqlite3', database: 'db.sqlite3'}, ['--mode', 'html'])
+ assert !aborted
+ assert_equal ['sqlite3', '-html', Rails.root.join('db.sqlite3').to_s], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_sqlite3_header
+ start({adapter: 'sqlite3', database: 'db.sqlite3'}, ['--header'])
+ assert_equal ['sqlite3', '-header', Rails.root.join('db.sqlite3').to_s], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_sqlite3_db_absolute_path
+ start(adapter: 'sqlite3', database: '/tmp/db.sqlite3')
+ assert !aborted
+ assert_equal ['sqlite3', '/tmp/db.sqlite3'], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_sqlite3_db_without_defined_rails_root
+ Rails.stub(:respond_to?, false) do
+ start(adapter: 'sqlite3', database: 'config/db.sqlite3')
+ assert !aborted
+ assert_equal ['sqlite3', Rails.root.join('../config/db.sqlite3').to_s], dbconsole.find_cmd_and_exec_args
+ end
+ end
+
+ def test_oracle
+ start(adapter: 'oracle', database: 'db', username: 'user', password: 'secret')
+ assert !aborted
+ assert_equal ['sqlplus', 'user@db'], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_oracle_include_password
+ start({adapter: 'oracle', database: 'db', username: 'user', password: 'secret'}, ['-p'])
+ assert !aborted
+ assert_equal ['sqlplus', 'user/secret@db'], dbconsole.find_cmd_and_exec_args
+ end
+
+ def test_unknown_command_line_client
+ start(adapter: 'unknown', database: 'db')
+ assert aborted
+ assert_match(/Unknown command-line client for db/, output)
+ end
+
+ def test_print_help_short
+ stdout = capture(:stdout) do
+ start({}, ['-h'])
+ end
+ assert aborted
+ assert_equal '', output
+ assert_match(/Usage:.*dbconsole/, stdout)
+ end
+
+ def test_print_help_long
+ stdout = capture(:stdout) do
+ start({}, ['--help'])
+ end
+ assert aborted
+ assert_equal '', output
+ assert_match(/Usage:.*dbconsole/, stdout)
+ end
+
+ attr_reader :aborted, :output
+ private :aborted, :output
+
+ private
+
+ def app_db_config(results)
+ Rails.application.config.stub(:database_configuration, results || {}) do
+ yield
+ end
+ end
+
+ def dbconsole
+ @dbconsole ||= Class.new(Rails::DBConsole) do
+ attr_reader :find_cmd_and_exec_args
+
+ def find_cmd_and_exec(*args)
+ @find_cmd_and_exec_args = args
+ end
+ end.new(nil)
+ end
+
+ def start(config = {}, argv = [])
+ dbconsole.stub(:config, config.stringify_keys) do
+ dbconsole.stub(:arguments, argv) do
+ capture_abort { dbconsole.start }
+ end
+ end
+ end
+
+ def capture_abort
+ @aborted = false
+ @output = capture(:stderr) do
+ begin
+ yield
+ rescue SystemExit
+ @aborted = true
+ end
+ end
+ end
+
+end
diff --git a/railties/test/commands/dev_cache_test.rb b/railties/test/commands/dev_cache_test.rb
new file mode 100644
index 0000000000..1b7a72e7fc
--- /dev/null
+++ b/railties/test/commands/dev_cache_test.rb
@@ -0,0 +1,32 @@
+require_relative '../isolation/abstract_unit'
+
+module CommandsTests
+ class DevCacheTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test 'dev:cache creates file and outputs message' do
+ Dir.chdir(app_path) do
+ output = `rails dev:cache`
+ assert File.exist?('tmp/caching-dev.txt')
+ assert_match(%r{Development mode is now being cached}, output)
+ end
+ end
+
+ test 'dev:cache deletes file and outputs message' do
+ Dir.chdir(app_path) do
+ `rails dev:cache` # Create caching file.
+ output = `rails dev:cache` # Delete caching file.
+ assert_not File.exist?('tmp/caching-dev.txt')
+ assert_match(%r{Development mode is no longer being cached}, output)
+ end
+ end
+ end
+end
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
new file mode 100644
index 0000000000..3be4a74f74
--- /dev/null
+++ b/railties/test/commands/server_test.rb
@@ -0,0 +1,111 @@
+require 'abstract_unit'
+require 'env_helpers'
+require 'rails/commands/server'
+
+class Rails::ServerTest < ActiveSupport::TestCase
+ include EnvHelpers
+
+ def test_environment_with_server_option
+ args = ["thin", "-e", "production"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal 'production', options[:environment]
+ assert_equal 'thin', options[:server]
+ end
+
+ def test_environment_without_server_option
+ args = ["-e", "production"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal 'production', options[:environment]
+ assert_nil options[:server]
+ end
+
+ def test_server_option_without_environment
+ args = ["thin"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_nil options[:environment]
+ assert_equal 'thin', options[:server]
+ end
+
+ def test_environment_with_rails_env
+ with_rack_env nil do
+ with_rails_env 'production' do
+ server = Rails::Server.new
+ assert_equal 'production', server.options[:environment]
+ end
+ end
+ end
+
+ def test_environment_with_rack_env
+ with_rails_env nil do
+ with_rack_env 'production' do
+ server = Rails::Server.new
+ assert_equal 'production', server.options[:environment]
+ end
+ end
+ end
+
+ def test_environment_with_port
+ switch_env "PORT", "1234" do
+ server = Rails::Server.new
+ assert_equal 1234, server.options[:Port]
+ end
+ end
+
+ def test_caching_without_option
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal nil, options[:caching]
+ end
+
+ def test_caching_with_option
+ args = ["--dev-caching"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:caching]
+
+ args = ["--no-dev-caching"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal false, options[:caching]
+ end
+
+ def test_log_stdout
+ with_rack_env nil do
+ with_rails_env nil do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
+
+ args = ["-e", "development"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
+
+ args = ["-e", "production"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal false, options[:log_stdout]
+
+ with_rack_env 'development' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
+ end
+
+ with_rack_env 'production' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal false, options[:log_stdout]
+ end
+
+ with_rails_env 'development' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
+ end
+
+ with_rails_env 'production' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal false, options[:log_stdout]
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/configuration/middleware_stack_proxy_test.rb b/railties/test/configuration/middleware_stack_proxy_test.rb
new file mode 100644
index 0000000000..d5072614cf
--- /dev/null
+++ b/railties/test/configuration/middleware_stack_proxy_test.rb
@@ -0,0 +1,61 @@
+require 'active_support'
+require 'active_support/testing/autorun'
+require 'rails/configuration'
+require 'active_support/test_case'
+require 'minitest/mock'
+
+module Rails
+ module Configuration
+ class MiddlewareStackProxyTest < ActiveSupport::TestCase
+ def setup
+ @stack = MiddlewareStackProxy.new
+ end
+
+ def test_playback_insert_before
+ @stack.insert_before :foo
+ assert_playback :insert_before, :foo
+ end
+
+ def test_playback_insert_after
+ @stack.insert_after :foo
+ assert_playback :insert_after, :foo
+ end
+
+ def test_playback_swap
+ @stack.swap :foo
+ assert_playback :swap, :foo
+ end
+
+ def test_playback_use
+ @stack.use :foo
+ assert_playback :use, :foo
+ end
+
+ def test_playback_delete
+ @stack.delete :foo
+ assert_playback :delete, :foo
+ end
+
+ def test_order
+ @stack.swap :foo
+ @stack.delete :foo
+
+ mock = Minitest::Mock.new
+ mock.expect :send, nil, [:swap, :foo]
+ mock.expect :send, nil, [:delete, :foo]
+
+ @stack.merge_into mock
+ mock.verify
+ end
+
+ private
+
+ def assert_playback(msg_name, args)
+ mock = Minitest::Mock.new
+ mock.expect :send, nil, [msg_name, args]
+ @stack.merge_into(mock)
+ mock.verify
+ end
+ end
+ end
+end
diff --git a/railties/test/engine_test.rb b/railties/test/engine_test.rb
new file mode 100644
index 0000000000..f46fb748f5
--- /dev/null
+++ b/railties/test/engine_test.rb
@@ -0,0 +1,25 @@
+require 'abstract_unit'
+
+class EngineTest < ActiveSupport::TestCase
+ test "reports routes as available only if they're actually present" do
+ engine = Class.new(Rails::Engine) do
+ def initialize(*args)
+ @routes = nil
+ super
+ end
+ end
+
+ assert !engine.routes?
+ end
+
+ def test_application_can_be_subclassed
+ klass = Class.new(Rails::Application) do
+ attr_reader :hello
+ def initialize
+ @hello = "world"
+ super
+ end
+ end
+ assert_equal "world", klass.instance.hello
+ end
+end
diff --git a/railties/test/env_helpers.rb b/railties/test/env_helpers.rb
new file mode 100644
index 0000000000..330fe150ca
--- /dev/null
+++ b/railties/test/env_helpers.rb
@@ -0,0 +1,30 @@
+require 'rails'
+
+module EnvHelpers
+ private
+
+ def with_rails_env(env)
+ Rails.instance_variable_set :@_env, nil
+ switch_env 'RAILS_ENV', env do
+ switch_env 'RACK_ENV', nil do
+ yield
+ end
+ end
+ end
+
+ def with_rack_env(env)
+ Rails.instance_variable_set :@_env, nil
+ switch_env 'RACK_ENV', env do
+ switch_env 'RAILS_ENV', nil do
+ yield
+ end
+ end
+ end
+
+ def switch_env(key, value)
+ old, ENV[key] = ENV[key], value
+ yield
+ ensure
+ ENV[key] = old
+ end
+end
diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml b/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml
new file mode 100644
index 0000000000..fe80872a16
--- /dev/null
+++ b/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml
@@ -0,0 +1 @@
+# an empty YAML file - any content in here seems to get parsed as a string \ No newline at end of file
diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb
new file mode 100644
index 0000000000..d4262f8971
--- /dev/null
+++ b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb
@@ -0,0 +1 @@
+# intentionally empty \ No newline at end of file
diff --git a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb
new file mode 100644
index 0000000000..d4262f8971
--- /dev/null
+++ b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb
@@ -0,0 +1 @@
+# intentionally empty \ No newline at end of file
diff --git a/railties/test/fixtures/lib/create_test_dummy_template.rb b/railties/test/fixtures/lib/create_test_dummy_template.rb
new file mode 100644
index 0000000000..e4378bbd1a
--- /dev/null
+++ b/railties/test/fixtures/lib/create_test_dummy_template.rb
@@ -0,0 +1 @@
+create_dummy_app("spec/dummy")
diff --git a/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb
new file mode 100644
index 0000000000..a7d079a1bc
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb
@@ -0,0 +1,8 @@
+require 'rails/generators/active_record'
+
+module ActiveRecord
+ module Generators
+ class FixjourGenerator < Base
+ end
+ end
+end
diff --git a/railties/test/fixtures/lib/generators/fixjour_generator.rb b/railties/test/fixtures/lib/generators/fixjour_generator.rb
new file mode 100644
index 0000000000..ef3e9edbed
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/fixjour_generator.rb
@@ -0,0 +1,2 @@
+class FixjourGenerator < Rails::Generators::NamedBase
+end
diff --git a/railties/test/fixtures/lib/generators/model_generator.rb b/railties/test/fixtures/lib/generators/model_generator.rb
new file mode 100644
index 0000000000..9098a8a354
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/model_generator.rb
@@ -0,0 +1 @@
+raise "I should never be loaded" \ No newline at end of file
diff --git a/railties/test/fixtures/lib/generators/usage_template/USAGE b/railties/test/fixtures/lib/generators/usage_template/USAGE
new file mode 100644
index 0000000000..bcd63c52e2
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/usage_template/USAGE
@@ -0,0 +1 @@
+:: <%= 1 + 1 %> :: \ No newline at end of file
diff --git a/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb
new file mode 100644
index 0000000000..078b0f9412
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb
@@ -0,0 +1,5 @@
+require 'rails/generators'
+
+class UsageTemplateGenerator < Rails::Generators::Base
+ source_root File.expand_path("templates", File.dirname(__FILE__))
+end
diff --git a/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb b/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb
new file mode 100644
index 0000000000..d1de8c56fa
--- /dev/null
+++ b/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb
@@ -0,0 +1,4 @@
+module Foobar
+ class FoobarGenerator < Rails::Generators::Base
+ end
+end
diff --git a/railties/test/fixtures/lib/template.rb b/railties/test/fixtures/lib/template.rb
new file mode 100644
index 0000000000..c14a1a8784
--- /dev/null
+++ b/railties/test/fixtures/lib/template.rb
@@ -0,0 +1 @@
+say "It works from file!"
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
new file mode 100644
index 0000000000..b4fbea4af4
--- /dev/null
+++ b/railties/test/generators/actions_test.rb
@@ -0,0 +1,316 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/app/app_generator'
+require 'env_helpers'
+
+class ActionsTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ include EnvHelpers
+
+ tests Rails::Generators::AppGenerator
+ arguments [destination_root]
+
+ def setup
+ Rails.application = TestApp::Application
+ super
+ end
+
+ def teardown
+ Rails.application = TestApp::Application.instance
+ end
+
+ def test_invoke_other_generator_with_shortcut
+ action :invoke, 'model', ['my_model']
+ assert_file 'app/models/my_model.rb', /MyModel/
+ end
+
+ def test_invoke_other_generator_with_full_namespace
+ action :invoke, 'rails:model', ['my_model']
+ assert_file 'app/models/my_model.rb', /MyModel/
+ end
+
+ def test_create_file_should_write_data_to_file_path
+ action :create_file, 'lib/test_file.rb', 'heres test data'
+ assert_file 'lib/test_file.rb', 'heres test data'
+ end
+
+ def test_create_file_should_write_block_contents_to_file_path
+ action(:create_file, 'lib/test_file.rb'){ 'heres block data' }
+ assert_file 'lib/test_file.rb', 'heres block data'
+ end
+
+ def test_add_source_adds_source_to_gemfile
+ run_generator
+ action :add_source, 'http://gems.github.com'
+ assert_file 'Gemfile', /source 'http:\/\/gems\.github\.com'/
+ end
+
+ def test_add_source_with_block_adds_source_to_gemfile_with_gem
+ run_generator
+ action :add_source, 'http://gems.github.com' do
+ gem 'rspec-rails'
+ end
+ assert_file 'Gemfile', /source 'http:\/\/gems\.github\.com' do\n gem 'rspec-rails'\nend/
+ end
+
+ def test_gem_should_put_gem_dependency_in_gemfile
+ run_generator
+ action :gem, 'will-paginate'
+ assert_file 'Gemfile', /gem 'will\-paginate'/
+ end
+
+ def test_gem_with_version_should_include_version_in_gemfile
+ run_generator
+
+ action :gem, 'rspec', '>=2.0.0.a5'
+
+ assert_file 'Gemfile', /gem 'rspec', '>=2.0.0.a5'/
+ end
+
+ def test_gem_should_insert_on_separate_lines
+ run_generator
+
+ File.open('Gemfile', 'a') {|f| f.write('# Some content...') }
+
+ action :gem, 'rspec'
+ action :gem, 'rspec-rails'
+
+ assert_file 'Gemfile', /^gem 'rspec'$/
+ assert_file 'Gemfile', /^gem 'rspec-rails'$/
+ end
+
+ def test_gem_should_include_options
+ run_generator
+
+ action :gem, 'rspec', github: 'dchelimsky/rspec', tag: '1.2.9.rc1'
+
+ assert_file 'Gemfile', /gem 'rspec', github: 'dchelimsky\/rspec', tag: '1\.2\.9\.rc1'/
+ end
+
+ def test_gem_with_non_string_options
+ run_generator
+
+ action :gem, 'rspec', require: false
+ action :gem, 'rspec-rails', group: [:development, :test]
+
+ assert_file 'Gemfile', /^gem 'rspec', require: false$/
+ assert_file 'Gemfile', /^gem 'rspec-rails', group: \[:development, :test\]$/
+ end
+
+ def test_gem_falls_back_to_inspect_if_string_contains_single_quote
+ run_generator
+
+ action :gem, 'rspec', ">=2.0'0"
+
+ assert_file 'Gemfile', /^gem 'rspec', ">=2\.0'0"$/
+ end
+
+ def test_gem_group_should_wrap_gems_in_a_group
+ run_generator
+
+ action :gem_group, :development, :test do
+ gem 'rspec-rails'
+ end
+
+ action :gem_group, :test do
+ gem 'fakeweb'
+ end
+
+ assert_file 'Gemfile', /\ngroup :development, :test do\n gem 'rspec-rails'\nend\n\ngroup :test do\n gem 'fakeweb'\nend/
+ end
+
+ def test_environment_should_include_data_in_environment_initializer_block
+ run_generator
+ autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
+ action :environment, autoload_paths
+ assert_file 'config/application.rb', / class Application < Rails::Application\n #{Regexp.escape(autoload_paths)}/
+ end
+
+ def test_environment_should_include_data_in_environment_initializer_block_with_env_option
+ run_generator
+ autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
+ action :environment, autoload_paths, env: 'development'
+ assert_file "config/environments/development.rb", /Rails\.application\.configure do\n #{Regexp.escape(autoload_paths)}/
+ end
+
+ def test_environment_with_block_should_include_block_contents_in_environment_initializer_block
+ run_generator
+
+ action :environment do
+ _ = '# This wont be added'# assignment to silence parse-time warning "unused literal ignored"
+ '# This will be added'
+ end
+
+ assert_file 'config/application.rb' do |content|
+ assert_match(/# This will be added/, content)
+ assert_no_match(/# This wont be added/, content)
+ end
+ end
+
+ def test_git_with_symbol_should_run_command_using_git_scm
+ assert_called_with(generator, :run, ['git init']) do
+ action :git, :init
+ end
+ end
+
+ def test_git_with_hash_should_run_each_command_using_git_scm
+ assert_called_with(generator, :run, [ ["git rm README"], ["git add ."] ]) do
+ action :git, rm: 'README', add: '.'
+ end
+ end
+
+ def test_vendor_should_write_data_to_file_in_vendor
+ action :vendor, 'vendor_file.rb', '# vendor data'
+ assert_file 'vendor/vendor_file.rb', '# vendor data'
+ end
+
+ def test_lib_should_write_data_to_file_in_lib
+ action :lib, 'my_library.rb', 'class MyLibrary'
+ assert_file 'lib/my_library.rb', 'class MyLibrary'
+ end
+
+ def test_rakefile_should_write_date_to_file_in_lib_tasks
+ action :rakefile, 'myapp.rake', 'task run: [:environment]'
+ assert_file 'lib/tasks/myapp.rake', 'task run: [:environment]'
+ end
+
+ def test_initializer_should_write_date_to_file_in_config_initializers
+ action :initializer, 'constants.rb', 'MY_CONSTANT = 42'
+ assert_file 'config/initializers/constants.rb', 'MY_CONSTANT = 42'
+ end
+
+ def test_generate_should_run_script_generate_with_argument_and_options
+ assert_called_with(generator, :run_ruby_script, ['bin/rails generate model MyModel', verbose: false]) do
+ action :generate, 'model', 'MyModel'
+ end
+ end
+
+ def test_rake_should_run_rake_command_with_default_env
+ assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=development", verbose: false]) do
+ with_rails_env nil do
+ action :rake, 'log:clear'
+ end
+ end
+ end
+
+ def test_rake_with_env_option_should_run_rake_command_in_env
+ assert_called_with(generator, :run, ['rake log:clear RAILS_ENV=production', verbose: false]) do
+ action :rake, 'log:clear', env: 'production'
+ end
+ end
+
+ def test_rake_with_rails_env_variable_should_run_rake_command_in_env
+ assert_called_with(generator, :run, ['rake log:clear RAILS_ENV=production', verbose: false]) do
+ with_rails_env "production" do
+ action :rake, 'log:clear'
+ end
+ end
+ end
+
+ def test_env_option_should_win_over_rails_env_variable_when_running_rake
+ assert_called_with(generator, :run, ['rake log:clear RAILS_ENV=production', verbose: false]) do
+ with_rails_env "staging" do
+ action :rake, 'log:clear', env: 'production'
+ end
+ end
+ end
+
+ def test_rake_with_sudo_option_should_run_rake_command_with_sudo
+ assert_called_with(generator, :run, ["sudo rake log:clear RAILS_ENV=development", verbose: false]) do
+ with_rails_env nil do
+ action :rake, 'log:clear', sudo: true
+ end
+ end
+ end
+
+ def test_capify_should_run_the_capify_command
+ assert_called_with(generator, :run, ['capify .', verbose: false]) do
+ action :capify!
+ end
+ end
+
+ def test_route_should_add_data_to_the_routes_block_in_config_routes
+ run_generator
+ route_command = "route '/login', controller: 'sessions', action: 'new'"
+ action :route, route_command
+ assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/
+ end
+
+ def test_route_should_be_idempotent
+ run_generator
+ route_path = File.expand_path('config/routes.rb', destination_root)
+
+ # runs first time, not asserting
+ action :route, "root 'welcome#index'"
+ content_1 = File.read(route_path)
+
+ # runs second time
+ action :route, "root 'welcome#index'"
+ content_2 = File.read(route_path)
+
+ assert_equal content_1, content_2
+ end
+
+ def test_route_should_add_data_with_an_new_line
+ run_generator
+ action :route, "root 'welcome#index'"
+ route_path = File.expand_path("config/routes.rb", destination_root)
+ content = File.read(route_path)
+
+ # Remove all of the comments and blank lines from the routes file
+ content.gsub!(/^ \#.*\n/, '')
+ content.gsub!(/^\n/, '')
+
+ File.open(route_path, "wb") { |file| file.write(content) }
+ assert_file "config/routes.rb", /\.routes\.draw do\n root 'welcome#index'\nend\n\z/
+
+ action :route, "resources :product_lines"
+
+ routes = <<-F
+Rails.application.routes.draw do
+ resources :product_lines
+ root 'welcome#index'
+end
+F
+ assert_file "config/routes.rb", routes
+ end
+
+ def test_readme
+ run_generator
+ assert_called(Rails::Generators::AppGenerator, :source_root, times: 2, returns: destination_root) do
+ assert_match "application up and running", action(:readme, "README.md")
+ end
+ end
+
+ def test_readme_with_quiet
+ generator(default_arguments, quiet: true)
+ run_generator
+ assert_called(Rails::Generators::AppGenerator, :source_root, times: 2, returns: destination_root) do
+ assert_no_match "application up and running", action(:readme, "README.md")
+ end
+ end
+
+ def test_log
+ assert_equal("YES\n", action(:log, "YES"))
+ end
+
+ def test_log_with_status
+ assert_equal(" yes YES\n", action(:log, :yes, "YES"))
+ end
+
+ def test_log_with_quiet
+ generator(default_arguments, quiet: true)
+ assert_equal("", action(:log, "YES"))
+ end
+
+ def test_log_with_status_with_quiet
+ generator(default_arguments, quiet: true)
+ assert_equal("", action(:log, :yes, "YES"))
+ end
+
+ protected
+
+ def action(*args, &block)
+ capture(:stdout){ generator.send(*args, &block) }
+ end
+
+end
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
new file mode 100644
index 0000000000..be2cd3a853
--- /dev/null
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -0,0 +1,98 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/app/app_generator'
+
+class ApiAppGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ tests Rails::Generators::AppGenerator
+
+ arguments [destination_root, '--api']
+
+ def setup
+ Rails.application = TestApp::Application
+ super
+
+ Kernel::silence_warnings do
+ Thor::Base.shell.send(:attr_accessor, :always_force)
+ @shell = Thor::Base.shell.new
+ @shell.send(:always_force=, true)
+ end
+ end
+
+ def teardown
+ super
+ Rails.application = TestApp::Application.instance
+ end
+
+ def test_skeleton_is_created
+ run_generator
+
+ default_files.each { |path| assert_file path }
+ skipped_files.each { |path| assert_no_file path }
+ end
+
+ def test_api_modified_files
+ run_generator
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/gem 'coffee-rails'/, content)
+ assert_no_match(/gem 'jquery-rails'/, content)
+ assert_no_match(/gem 'sass-rails'/, content)
+ assert_no_match(/gem 'jbuilder'/, content)
+ assert_no_match(/gem 'web-console'/, content)
+ assert_match(/gem 'active_model_serializers'/, content)
+ end
+
+ assert_file "config/application.rb" do |content|
+ assert_match(/config.api_only = true/, content)
+ end
+
+ assert_file "config/initializers/cors.rb"
+
+ assert_file "config/initializers/wrap_parameters.rb"
+
+ assert_file "app/controllers/application_controller.rb", /ActionController::API/
+ end
+
+ private
+
+ def default_files
+ files = %W(
+ .gitignore
+ Gemfile
+ Rakefile
+ config.ru
+ app/controllers
+ app/mailers
+ app/models
+ config/environments
+ config/initializers
+ config/locales
+ db
+ lib
+ lib/tasks
+ log
+ test/fixtures
+ test/controllers
+ test/integration
+ test/models
+ tmp
+ vendor
+ )
+ files.concat %w(bin/bundle bin/rails bin/rake)
+ files
+ end
+
+ def skipped_files
+ %w(app/assets
+ app/helpers
+ app/views
+ config/initializers/assets.rb
+ config/initializers/cookies_serializer.rb
+ config/initializers/session_store.rb
+ config/initializers/request_forgery_protection.rb
+ lib/assets
+ vendor/assets
+ test/helpers
+ tmp/cache/assets)
+ end
+end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
new file mode 100644
index 0000000000..dd2e931c5c
--- /dev/null
+++ b/railties/test/generators/app_generator_test.rb
@@ -0,0 +1,696 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/app/app_generator'
+require 'generators/shared_generator_tests'
+
+DEFAULT_APP_FILES = %w(
+ .gitignore
+ README.md
+ Gemfile
+ Rakefile
+ config.ru
+ app/assets/javascripts
+ app/assets/stylesheets
+ app/assets/images
+ app/controllers
+ app/controllers/concerns
+ app/helpers
+ app/mailers
+ app/models
+ app/models/concerns
+ app/jobs
+ app/views/layouts
+ bin/bundle
+ bin/rails
+ bin/rake
+ bin/setup
+ config/environments
+ config/initializers
+ config/locales
+ db
+ lib
+ lib/tasks
+ lib/assets
+ log
+ test/test_helper.rb
+ test/fixtures
+ test/fixtures/files
+ test/controllers
+ test/models
+ test/helpers
+ test/mailers
+ test/integration
+ vendor
+ vendor/assets
+ vendor/assets/stylesheets
+ vendor/assets/javascripts
+ tmp
+ tmp/cache
+ tmp/cache/assets
+)
+
+class AppGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments [destination_root]
+
+ # brings setup, teardown, and some tests
+ include SharedGeneratorTests
+
+ def default_files
+ ::DEFAULT_APP_FILES
+ end
+
+ def test_assets
+ run_generator
+
+ assert_file("app/views/layouts/application.html.erb", /stylesheet_link_tag\s+'application', media: 'all', 'data-turbolinks-track' => true/)
+ assert_file("app/views/layouts/application.html.erb", /javascript_include_tag\s+'application', 'data-turbolinks-track' => true/)
+ assert_file("app/assets/stylesheets/application.css")
+ assert_file("app/assets/javascripts/application.js")
+ end
+
+ def test_application_job_file_present
+ run_generator
+ assert_file("app/jobs/application_job.rb")
+ end
+
+ def test_invalid_application_name_raises_an_error
+ content = capture(:stderr){ run_generator [File.join(destination_root, "43-things")] }
+ assert_equal "Invalid application name 43-things. Please give a name which does not start with numbers.\n", content
+ end
+
+ def test_invalid_application_name_is_fixed
+ run_generator [File.join(destination_root, "things-43")]
+ assert_file "things-43/config/environment.rb", /Rails\.application\.initialize!/
+ assert_file "things-43/config/application.rb", /^module Things43$/
+ end
+
+ def test_application_new_exits_with_non_zero_code_on_invalid_application_name
+ quietly { system 'rails new test --no-rc' }
+ assert_equal false, $?.success?
+ end
+
+ def test_application_new_exits_with_message_and_non_zero_code_when_generating_inside_existing_rails_directory
+ app_root = File.join(destination_root, 'myfirstapp')
+ run_generator [app_root]
+ output = nil
+ Dir.chdir(app_root) do
+ output = `rails new mysecondapp`
+ end
+ assert_equal "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\nType 'rails' for help.\n", output
+ assert_equal false, $?.success?
+ end
+
+ def test_application_new_show_help_message_inside_existing_rails_directory
+ app_root = File.join(destination_root, 'myfirstapp')
+ run_generator [app_root]
+ output = Dir.chdir(app_root) do
+ `rails new --help`
+ end
+ assert_match(/rails new APP_PATH \[options\]/, output)
+ assert_equal true, $?.success?
+ end
+
+ def test_application_name_is_detected_if_it_exists_and_app_folder_renamed
+ app_root = File.join(destination_root, "myapp")
+ app_moved_root = File.join(destination_root, "myapp_moved")
+
+ run_generator [app_root]
+
+ stub_rails_application(app_moved_root) do
+ Rails.application.stub(:is_a?, -> *args { Rails::Application }) do
+ FileUtils.mv(app_root, app_moved_root)
+
+ # make sure we are in correct dir
+ FileUtils.cd(app_moved_root)
+
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true },
+ destination_root: app_moved_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_file "myapp_moved/config/environment.rb", /Rails\.application\.initialize!/
+ assert_file "myapp_moved/config/initializers/session_store.rb", /_myapp_session/
+ end
+ end
+ end
+
+ def test_rails_update_generates_correct_session_key
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_file "myapp/config/initializers/session_store.rb", /_myapp_session/
+ end
+ end
+
+ def test_new_application_use_json_serialzier
+ run_generator
+
+ assert_file("config/initializers/cookies_serializer.rb", /Rails\.application\.config\.action_dispatch\.cookies_serializer = :json/)
+ end
+
+ def test_rails_update_keep_the_cookie_serializer_if_it_is_already_configured
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, 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/)
+ end
+ end
+
+ def test_rails_update_does_not_create_callback_terminator_initializer
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ FileUtils.rm("#{app_root}/config/initializers/callback_terminator.rb")
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_no_file "#{app_root}/config/initializers/callback_terminator.rb"
+ end
+ end
+
+ def test_rails_update_does_not_remove_callback_terminator_initializer_if_already_present
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ FileUtils.touch("#{app_root}/config/initializers/callback_terminator.rb")
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_file "#{app_root}/config/initializers/callback_terminator.rb"
+ end
+ end
+
+ def test_rails_update_set_the_cookie_serializer_to_marchal_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"], { with_dispatchers: true }, 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 = :marshal/)
+ end
+ end
+
+ def test_rails_update_does_not_create_active_record_belongs_to_required_by_default
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ FileUtils.rm("#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb")
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_no_file "#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb"
+ end
+ end
+
+ def test_rails_update_does_not_remove_active_record_belongs_to_required_by_default_if_already_present
+ app_root = File.join(destination_root, 'myapp')
+ run_generator [app_root]
+
+ FileUtils.touch("#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb")
+
+ stub_rails_application(app_root) do
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
+ generator.send(:app_const)
+ quietly { generator.send(:update_config_files) }
+ assert_file "#{app_root}/config/initializers/active_record_belongs_to_required_by_default.rb"
+ end
+ end
+
+ def test_application_names_are_not_singularized
+ run_generator [File.join(destination_root, "hats")]
+ assert_file "hats/config/environment.rb", /Rails\.application\.initialize!/
+ end
+
+ def test_gemfile_has_no_whitespace_errors
+ run_generator
+ absolute = File.expand_path("Gemfile", destination_root)
+ File.open(absolute, 'r') do |f|
+ f.each_line do |line|
+ assert_no_match %r{/^[ \t]+$/}, line
+ end
+ end
+ end
+
+ def test_config_database_is_added_by_default
+ run_generator
+ assert_file "config/database.yml", /sqlite3/
+ if defined?(JRUBY_VERSION)
+ assert_gem "activerecord-jdbcsqlite3-adapter"
+ else
+ assert_gem "sqlite3"
+ end
+ end
+
+ def test_config_another_database
+ run_generator([destination_root, "-d", "mysql"])
+ assert_file "config/database.yml", /mysql/
+ if defined?(JRUBY_VERSION)
+ assert_gem "activerecord-jdbcmysql-adapter"
+ else
+ assert_gem "mysql2", "'>= 0.3.18', '< 0.5'"
+ end
+ end
+
+ def test_config_database_app_name_with_period
+ run_generator [File.join(destination_root, "common.usage.com"), "-d", "postgresql"]
+ assert_file "common.usage.com/config/database.yml", /common_usage_com/
+ end
+
+ def test_config_postgresql_database
+ run_generator([destination_root, "-d", "postgresql"])
+ assert_file "config/database.yml", /postgresql/
+ if defined?(JRUBY_VERSION)
+ assert_gem "activerecord-jdbcpostgresql-adapter"
+ else
+ assert_gem "pg", "'~> 0.18'"
+ end
+ end
+
+ def test_config_jdbcmysql_database
+ run_generator([destination_root, "-d", "jdbcmysql"])
+ assert_file "config/database.yml", /mysql/
+ assert_gem "activerecord-jdbcmysql-adapter"
+ end
+
+ def test_config_jdbcsqlite3_database
+ run_generator([destination_root, "-d", "jdbcsqlite3"])
+ assert_file "config/database.yml", /sqlite3/
+ assert_gem "activerecord-jdbcsqlite3-adapter"
+ end
+
+ def test_config_jdbcpostgresql_database
+ run_generator([destination_root, "-d", "jdbcpostgresql"])
+ assert_file "config/database.yml", /postgresql/
+ assert_gem "activerecord-jdbcpostgresql-adapter"
+ end
+
+ def test_config_jdbc_database
+ run_generator([destination_root, "-d", "jdbc"])
+ assert_file "config/database.yml", /jdbc/
+ assert_file "config/database.yml", /mssql/
+ assert_gem "activerecord-jdbc-adapter"
+ end
+
+ if defined?(JRUBY_VERSION)
+ def test_config_jdbc_database_when_no_option_given
+ run_generator
+ assert_file "config/database.yml", /sqlite3/
+ assert_gem "activerecord-jdbcsqlite3-adapter"
+ end
+ end
+
+ def test_generator_without_skips
+ run_generator
+ assert_file "config/application.rb", /\s+require\s+["']rails\/all["']/
+ assert_file "config/environments/development.rb" do |content|
+ assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content)
+ end
+ assert_file "config/environments/test.rb" do |content|
+ assert_match(/config\.action_mailer\.delivery_method = :test/, content)
+ end
+ assert_file "config/environments/production.rb" do |content|
+ assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content)
+ end
+ end
+
+ def test_generator_if_skip_active_record_is_given
+ run_generator [destination_root, "--skip-active-record"]
+ assert_no_file "config/database.yml"
+ assert_no_file "config/initializers/active_record_belongs_to_required_by_default.rb"
+ assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
+ assert_file "test/test_helper.rb" do |helper_content|
+ assert_no_match(/fixtures :all/, helper_content)
+ end
+ end
+
+ def test_generator_if_skip_action_mailer_is_given
+ run_generator [destination_root, "--skip-action-mailer"]
+ assert_file "config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/
+ assert_file "config/environments/development.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ assert_file "config/environments/test.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ assert_file "config/environments/production.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ end
+
+ def test_generator_if_skip_sprockets_is_given
+ run_generator [destination_root, "--skip-sprockets"]
+ assert_no_file "config/initializers/assets.rb"
+ assert_file "config/application.rb" do |content|
+ assert_match(/#\s+require\s+["']sprockets\/railtie["']/, content)
+ end
+ assert_file "Gemfile" do |content|
+ assert_no_match(/sass-rails/, content)
+ assert_no_match(/uglifier/, content)
+ assert_match(/coffee-rails/, content)
+ end
+ assert_file "config/environments/development.rb" do |content|
+ assert_no_match(/config\.assets\.debug = true/, content)
+ end
+ assert_file "config/environments/production.rb" do |content|
+ assert_no_match(/config\.assets\.digest = true/, content)
+ assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
+ assert_no_match(/config\.assets\.css_compressor = :sass/, content)
+ end
+ end
+
+ def test_inclusion_of_javascript_runtime
+ run_generator
+ if defined?(JRUBY_VERSION)
+ assert_gem "therubyrhino"
+ else
+ assert_file "Gemfile", /# gem 'therubyracer', platforms: :ruby/
+ end
+ end
+
+ def test_jquery_is_the_default_javascript_library
+ run_generator
+ assert_file "app/assets/javascripts/application.js" do |contents|
+ assert_match %r{^//= require jquery}, contents
+ assert_match %r{^//= require jquery_ujs}, contents
+ end
+ assert_gem "jquery-rails"
+ end
+
+ def test_other_javascript_libraries
+ run_generator [destination_root, '-j', 'prototype']
+ assert_file "app/assets/javascripts/application.js" do |contents|
+ assert_match %r{^//= require prototype}, contents
+ assert_match %r{^//= require prototype_ujs}, contents
+ end
+ assert_gem "prototype-rails"
+ end
+
+ def test_javascript_is_skipped_if_required
+ run_generator [destination_root, "--skip-javascript"]
+
+ assert_no_file "app/assets/javascripts"
+ assert_no_file "vendor/assets/javascripts"
+
+ assert_file "app/views/layouts/application.html.erb" do |contents|
+ assert_match(/stylesheet_link_tag\s+'application', media: 'all' %>/, contents)
+ assert_no_match(/javascript_include_tag\s+'application' \%>/, contents)
+ end
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/coffee-rails/, content)
+ assert_no_match(/jquery-rails/, content)
+ end
+ end
+
+ def test_inclusion_of_jbuilder
+ run_generator
+ assert_gem 'jbuilder'
+ end
+
+ def test_inclusion_of_a_debugger
+ run_generator
+ if defined?(JRUBY_VERSION) || RUBY_ENGINE == "rbx"
+ assert_file "Gemfile" do |content|
+ assert_no_match(/byebug/, content)
+ end
+ else
+ assert_gem 'byebug'
+ end
+ end
+
+ def test_template_from_dir_pwd
+ FileUtils.cd(Rails.root)
+ assert_match(/It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"]))
+ end
+
+ def test_usage_read_from_file
+ assert_called(File, :read, returns: "USAGE FROM FILE") do
+ assert_equal "USAGE FROM FILE", Rails::Generators::AppGenerator.desc
+ end
+ end
+
+ def test_default_usage
+ assert_called(Rails::Generators::AppGenerator, :usage_path, returns: nil) do
+ assert_match(/Create rails files for app generator/, Rails::Generators::AppGenerator.desc)
+ end
+ end
+
+ def test_default_namespace
+ assert_match "rails:app", Rails::Generators::AppGenerator.namespace
+ end
+
+ def test_file_is_added_for_backwards_compatibility
+ action :file, 'lib/test_file.rb', 'heres test data'
+ assert_file 'lib/test_file.rb', 'heres test data'
+ end
+
+ def test_tests_are_removed_from_frameworks_if_skip_test_is_given
+ run_generator [destination_root, "--skip-test"]
+ assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/
+ end
+
+ def test_no_active_record_or_tests_if_skips_given
+ run_generator [destination_root, "--skip-test", "--skip-active-record"]
+ assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/
+ assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
+ assert_file "config/application.rb", /\s+require\s+["']active_job\/railtie["']/
+ end
+
+ def test_new_hash_style
+ run_generator
+ assert_file "config/initializers/session_store.rb" do |file|
+ assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file)
+ end
+ end
+
+ def test_pretend_option
+ output = run_generator [File.join(destination_root, "myapp"), "--pretend"]
+ assert_no_match(/run bundle install/, output)
+ end
+
+ def test_application_name_with_spaces
+ path = File.join(destination_root, "foo bar")
+
+ # This also applies to MySQL apps but not with SQLite
+ run_generator [path, "-d", 'postgresql']
+
+ assert_file "foo bar/config/database.yml", /database: foo_bar_development/
+ assert_file "foo bar/config/initializers/session_store.rb", /key: '_foo_bar/
+ end
+
+ def test_web_console
+ run_generator
+ assert_gem 'web-console'
+ end
+
+ def test_web_console_with_dev_option
+ run_generator [destination_root, "--dev"]
+
+ assert_file "Gemfile" do |content|
+ assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content)
+ assert_no_match(/gem 'web-console', '~> 3.0'/, content)
+ end
+ end
+
+ def test_web_console_with_edge_option
+ run_generator [destination_root, "--edge"]
+
+ assert_file "Gemfile" do |content|
+ assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content)
+ assert_no_match(/gem 'web-console', '~> 3.0'/, content)
+ end
+ end
+
+ def test_spring
+ run_generator
+ assert_gem 'spring'
+ end
+
+ def test_spring_binstubs
+ jruby_skip "spring doesn't run on JRuby"
+ command_check = -> command do
+ @binstub_called ||= 0
+
+ case command
+ when 'install'
+ # Called when running bundle, we just want to stub it so nothing to do here.
+ when 'exec spring binstub --all'
+ @binstub_called += 1
+ assert_equal 1, @binstub_called, "exec spring binstub --all expected to be called once, but was called #{@install_called} times."
+ end
+ end
+
+ generator.stub :bundle_command, command_check do
+ quietly { generator.invoke_all }
+ end
+ end
+
+ def test_spring_no_fork
+ jruby_skip "spring doesn't run on JRuby"
+ assert_called_with(Process, :respond_to?, [:fork], returns: false) do
+ run_generator
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/spring/, content)
+ end
+ end
+ end
+
+ def test_skip_spring
+ run_generator [destination_root, "--skip-spring"]
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/spring/, content)
+ end
+ end
+
+ def test_spring_with_dev_option
+ run_generator [destination_root, "--dev"]
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/spring/, content)
+ end
+ end
+
+ def test_generator_if_skip_turbolinks_is_given
+ run_generator [destination_root, "--skip-turbolinks"]
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/turbolinks/, content)
+ end
+ assert_file "app/views/layouts/application.html.erb" do |content|
+ assert_no_match(/data-turbolinks-track/, content)
+ end
+ assert_file "app/assets/javascripts/application.js" do |content|
+ assert_no_match(/turbolinks/, content)
+ end
+ end
+
+ def test_gitignore_when_sqlite3
+ run_generator
+
+ assert_file '.gitignore' do |content|
+ assert_match(/sqlite3/, content)
+ end
+ end
+
+ def test_gitignore_when_no_active_record
+ run_generator [destination_root, '--skip-active-record']
+
+ assert_file '.gitignore' do |content|
+ assert_no_match(/sqlite/i, content)
+ end
+ end
+
+ def test_gitignore_when_non_sqlite3_db
+ run_generator([destination_root, "-d", "mysql"])
+
+ assert_file '.gitignore' do |content|
+ assert_no_match(/sqlite/i, content)
+ end
+ end
+
+ def test_create_keeps
+ run_generator
+ folders_with_keep = %w(
+ app/assets/images
+ app/mailers
+ app/models
+ app/controllers/concerns
+ app/models/concerns
+ lib/tasks
+ lib/assets
+ log
+ test/fixtures
+ test/fixtures/files
+ test/controllers
+ test/mailers
+ test/models
+ test/helpers
+ test/integration
+ tmp
+ vendor/assets/stylesheets
+ )
+ folders_with_keep.each do |folder|
+ assert_file("#{folder}/.keep")
+ end
+ end
+
+ def test_psych_gem
+ run_generator
+ gem_regex = /gem 'psych',\s+'~> 2.0',\s+platforms: :rbx/
+
+ assert_file "Gemfile" do |content|
+ if defined?(Rubinius)
+ assert_match(gem_regex, content)
+ else
+ assert_no_match(gem_regex, content)
+ end
+ end
+ end
+
+ def test_after_bundle_callback
+ path = 'http://example.org/rails_template'
+ template = %{ after_bundle { run 'echo ran after_bundle' } }
+ template.instance_eval "def read; self; end" # Make the string respond to read
+
+ check_open = -> *args do
+ assert_equal [ path, 'Accept' => 'application/x-thor-template' ], args
+ template
+ end
+
+ sequence = ['install', 'exec spring binstub --all', 'echo ran after_bundle']
+ ensure_bundler_first = -> command do
+ @sequence_step ||= 0
+
+ assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}"
+ @sequence_step += 1
+ end
+
+ generator([destination_root], template: path).stub(:open, check_open, template) do
+ generator.stub(:bundle_command, ensure_bundler_first) do
+ generator.stub(:run, ensure_bundler_first) do
+ quietly { generator.invoke_all }
+ end
+ end
+ end
+ end
+
+ protected
+
+ def stub_rails_application(root)
+ Rails.application.config.root = root
+ Rails.application.class.stub(:name, "Myapp") do
+ yield
+ end
+ end
+
+ def action(*args, &block)
+ capture(:stdout) { generator.send(*args, &block) }
+ end
+
+ def assert_gem(gem, constraint = nil)
+ if constraint
+ assert_file "Gemfile", /^\s*gem\s+["']#{gem}["'], #{constraint}$*/
+ else
+ assert_file "Gemfile", /^\s*gem\s+["']#{gem}["']$*/
+ end
+ end
+end
diff --git a/railties/test/generators/argv_scrubber_test.rb b/railties/test/generators/argv_scrubber_test.rb
new file mode 100644
index 0000000000..31e07bc8da
--- /dev/null
+++ b/railties/test/generators/argv_scrubber_test.rb
@@ -0,0 +1,136 @@
+require 'active_support/test_case'
+require 'active_support/testing/autorun'
+require 'rails/generators/rails/app/app_generator'
+require 'tempfile'
+
+module Rails
+ module Generators
+ class ARGVScrubberTest < ActiveSupport::TestCase # :nodoc:
+ # Future people who read this... These tests are just to surround the
+ # current behavior of the ARGVScrubber, they do not mean that the class
+ # *must* act this way, I just want to prevent regressions.
+
+ def test_version
+ ['-v', '--version'].each do |str|
+ scrubber = ARGVScrubber.new [str]
+ output = nil
+ exit_code = nil
+ scrubber.extend(Module.new {
+ define_method(:puts) { |string| output = string }
+ define_method(:exit) { |code| exit_code = code }
+ })
+ scrubber.prepare!
+ assert_equal "Rails #{Rails::VERSION::STRING}", output
+ assert_equal 0, exit_code
+ end
+ end
+
+ def test_default_help
+ argv = ['zomg', 'how', 'are', 'you']
+ scrubber = ARGVScrubber.new argv
+ args = scrubber.prepare!
+ assert_equal ['--help'] + argv.drop(1), args
+ end
+
+ def test_prepare_returns_args
+ scrubber = ARGVScrubber.new ['hi mom']
+ args = scrubber.prepare!
+ assert_equal '--help', args.first
+ end
+
+ def test_no_mutations
+ scrubber = ARGVScrubber.new ['hi mom'].freeze
+ args = scrubber.prepare!
+ assert_equal '--help', args.first
+ end
+
+ def test_new_command_no_rc
+ scrubber = Class.new(ARGVScrubber) {
+ def self.default_rc_file
+ File.join(Dir.tmpdir, 'whatever')
+ end
+ }.new ['new']
+ args = scrubber.prepare!
+ assert_equal [], args
+ end
+
+ def test_new_homedir_rc
+ file = Tempfile.new 'myrcfile'
+ file.puts '--hello-world'
+ file.flush
+
+ message = nil
+ scrubber = Class.new(ARGVScrubber) {
+ define_singleton_method(:default_rc_file) do
+ file.path
+ end
+ define_method(:puts) { |msg| message = msg }
+ }.new ['new']
+ args = scrubber.prepare!
+ assert_equal ['--hello-world'], args
+ assert_match 'hello-world', message
+ assert_match file.path, message
+ ensure
+ file.close
+ file.unlink
+ end
+
+ def test_rc_whitespace_separated
+ file = Tempfile.new 'myrcfile'
+ file.puts '--hello --world'
+ file.flush
+
+ message = nil
+ scrubber = Class.new(ARGVScrubber) {
+ define_method(:puts) { |msg| message = msg }
+ }.new ['new', "--rc=#{file.path}"]
+ args = scrubber.prepare!
+ assert_equal ['--hello', '--world'], args
+ ensure
+ file.close
+ file.unlink
+ end
+
+ def test_new_rc_option
+ file = Tempfile.new 'myrcfile'
+ file.puts '--hello-world'
+ file.flush
+
+ message = nil
+ scrubber = Class.new(ARGVScrubber) {
+ define_method(:puts) { |msg| message = msg }
+ }.new ['new', "--rc=#{file.path}"]
+ args = scrubber.prepare!
+ assert_equal ['--hello-world'], args
+ assert_match 'hello-world', message
+ assert_match file.path, message
+ ensure
+ file.close
+ file.unlink
+ end
+
+ def test_new_rc_option_and_custom_options
+ file = Tempfile.new 'myrcfile'
+ file.puts '--hello'
+ file.puts '--world'
+ file.flush
+
+ scrubber = Class.new(ARGVScrubber) {
+ define_method(:puts) { |msg| }
+ }.new ['new', 'tenderapp', '--love', "--rc=#{file.path}"]
+
+ args = scrubber.prepare!
+ assert_equal ["tenderapp", "--hello", "--world", "--love"], args
+ ensure
+ file.close
+ file.unlink
+ end
+
+ def test_no_rc
+ scrubber = ARGVScrubber.new ['new', '--no-rc']
+ args = scrubber.prepare!
+ assert_equal [], args
+ end
+ end
+ end
+end
diff --git a/railties/test/generators/assets_generator_test.rb b/railties/test/generators/assets_generator_test.rb
new file mode 100644
index 0000000000..a2b94f2e50
--- /dev/null
+++ b/railties/test/generators/assets_generator_test.rb
@@ -0,0 +1,19 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/assets/assets_generator'
+
+class AssetsGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(posts)
+
+ def test_assets
+ run_generator
+ assert_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_skipping_assets
+ run_generator ["posts", "--no-stylesheets", "--no-javascripts"]
+ assert_no_file "app/assets/javascripts/posts.js"
+ assert_no_file "app/assets/stylesheets/posts.css"
+ end
+end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
new file mode 100644
index 0000000000..1351151afb
--- /dev/null
+++ b/railties/test/generators/controller_generator_test.rb
@@ -0,0 +1,103 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/controller/controller_generator'
+
+class ControllerGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(Account foo bar)
+
+ setup :copy_routes
+
+ def test_help_does_not_show_invoked_generators_options_if_they_already_exist
+ content = run_generator ["--help"]
+ assert_no_match(/Helper options\:/, content)
+ end
+
+ def test_controller_skeleton_is_created
+ run_generator
+ assert_file "app/controllers/account_controller.rb", /class AccountController < ApplicationController/
+ end
+
+ def test_check_class_collision
+ Object.send :const_set, :ObjectController, Class.new
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match(/The name 'ObjectController' is either already used in your application or reserved/, content)
+ ensure
+ Object.send :remove_const, :ObjectController
+ end
+
+ def test_invokes_helper
+ run_generator
+ assert_file "app/helpers/account_helper.rb"
+ end
+
+ def test_does_not_invoke_helper_if_required
+ run_generator ["account", "--skip-helper"]
+ assert_no_file "app/helpers/account_helper.rb"
+ end
+
+ def test_invokes_assets
+ run_generator
+ assert_file "app/assets/javascripts/account.js"
+ assert_file "app/assets/stylesheets/account.css"
+ end
+
+ def test_does_not_invoke_assets_if_required
+ run_generator ["account", "--skip-assets"]
+ assert_no_file "app/assets/javascripts/account.js"
+ assert_no_file "app/assets/stylesheets/account.css"
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/controllers/account_controller_test.rb"
+ end
+
+ def test_does_not_invoke_test_framework_if_required
+ run_generator ["account", "--no-test-framework"]
+ assert_no_file "test/controllers/account_controller_test.rb"
+ end
+
+ def test_invokes_default_template_engine
+ run_generator
+ assert_file "app/views/account/foo.html.erb", %r(app/views/account/foo\.html\.erb)
+ assert_file "app/views/account/bar.html.erb", %r(app/views/account/bar\.html\.erb)
+ end
+
+ def test_add_routes
+ run_generator
+ assert_file "config/routes.rb", /get 'account\/foo'/, /get 'account\/bar'/
+ end
+
+ def test_skip_routes
+ run_generator ["account", "foo", "--skip-routes"]
+ assert_file "config/routes.rb" do |routes|
+ assert_no_match(/get 'account\/foo'/, routes)
+ end
+ end
+
+ def test_invokes_default_template_engine_even_with_no_action
+ run_generator ["account"]
+ assert_file "app/views/account"
+ end
+
+ def test_template_engine_with_class_path
+ run_generator ["admin/account"]
+ assert_file "app/views/admin/account"
+ end
+
+ def test_actions_are_turned_into_methods
+ run_generator
+
+ assert_file "app/controllers/account_controller.rb" do |controller|
+ assert_instance_method :foo, controller
+ assert_instance_method :bar, controller
+ end
+ end
+
+ def test_namespaced_routes_are_created_in_routes
+ run_generator ["admin/dashboard", "index"]
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n get 'dashboard\/index'\n end$/, route)
+ end
+ end
+end
diff --git a/railties/test/generators/create_migration_test.rb b/railties/test/generators/create_migration_test.rb
new file mode 100644
index 0000000000..e16a77479a
--- /dev/null
+++ b/railties/test/generators/create_migration_test.rb
@@ -0,0 +1,134 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/migration/migration_generator'
+
+class CreateMigrationTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ class Migrator < Rails::Generators::MigrationGenerator
+ include Rails::Generators::Migration
+
+ def self.next_migration_number(dirname)
+ current_migration_number(dirname) + 1
+ end
+ end
+
+ tests Migrator
+
+ def default_destination_path
+ "db/migrate/create_articles.rb"
+ end
+
+ def create_migration(destination_path = default_destination_path, config = {}, generator_options = {}, &block)
+ migration_name = File.basename(destination_path, '.rb')
+ generator([migration_name], generator_options)
+ generator.set_migration_assigns!(destination_path)
+
+ dir, base = File.split(destination_path)
+ timestamped_destination_path = File.join(dir, ["%migration_number%", base].join('_'))
+
+ @migration = Rails::Generators::Actions::CreateMigration.new(generator, timestamped_destination_path, block || "contents", config)
+ end
+
+ def migration_exists!(*args)
+ @existing_migration = create_migration(*args)
+ invoke!
+ @generator = nil
+ end
+
+ def invoke!
+ capture(:stdout) { @migration.invoke! }
+ end
+
+ def revoke!
+ capture(:stdout) { @migration.revoke! }
+ end
+
+ def test_invoke
+ create_migration
+
+ assert_match(/create db\/migrate\/1_create_articles.rb\n/, invoke!)
+ assert_file @migration.destination
+ end
+
+ def test_invoke_pretended
+ create_migration(default_destination_path, {}, { pretend: true })
+
+ assert_no_file @migration.destination
+ end
+
+ def test_invoke_when_exists
+ migration_exists!
+ create_migration
+
+ assert_equal @existing_migration.destination, @migration.existing_migration
+ end
+
+ def test_invoke_when_exists_identical
+ migration_exists!
+ create_migration
+
+ assert_match(/identical db\/migrate\/1_create_articles.rb\n/, invoke!)
+ assert @migration.identical?
+ end
+
+ def test_invoke_when_exists_not_identical
+ migration_exists!
+ create_migration { "different content" }
+
+ assert_raise(Rails::Generators::Error) { invoke! }
+ end
+
+ def test_invoke_forced_when_exists_not_identical
+ dest = "db/migrate/migration.rb"
+ migration_exists!(dest)
+ create_migration(dest, force: true) { "different content" }
+
+ stdout = invoke!
+ assert_match(/remove db\/migrate\/1_migration.rb\n/, stdout)
+ assert_match(/create db\/migrate\/2_migration.rb\n/, stdout)
+ assert_file @migration.destination
+ assert_no_file @existing_migration.destination
+ end
+
+ def test_invoke_forced_pretended_when_exists_not_identical
+ migration_exists!
+ create_migration(default_destination_path, { force: true }, { pretend: true }) do
+ "different content"
+ end
+
+ stdout = invoke!
+ assert_match(/remove db\/migrate\/1_create_articles.rb\n/, stdout)
+ assert_match(/create db\/migrate\/2_create_articles.rb\n/, stdout)
+ assert_no_file @migration.destination
+ end
+
+ def test_invoke_skipped_when_exists_not_identical
+ migration_exists!
+ create_migration(default_destination_path, {}, { skip: true }) { "different content" }
+
+ assert_match(/skip db\/migrate\/2_create_articles.rb\n/, invoke!)
+ assert_no_file @migration.destination
+ end
+
+ def test_revoke
+ migration_exists!
+ create_migration
+
+ assert_match(/remove db\/migrate\/1_create_articles.rb\n/, revoke!)
+ assert_no_file @existing_migration.destination
+ end
+
+ def test_revoke_pretended
+ migration_exists!
+ create_migration(default_destination_path, {}, { pretend: true })
+
+ assert_match(/remove db\/migrate\/1_create_articles.rb\n/, revoke!)
+ assert_file @existing_migration.destination
+ end
+
+ def test_revoke_when_no_exists
+ create_migration
+
+ assert_match(/remove db\/migrate\/1_create_articles.rb\n/, revoke!)
+ end
+end
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
new file mode 100644
index 0000000000..ee7c009305
--- /dev/null
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -0,0 +1,152 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/generated_attribute'
+
+class GeneratedAttributeTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ def test_field_type_returns_number_field
+ assert_field_type :integer, :number_field
+ end
+
+ def test_field_type_returns_text_field
+ %w(float decimal string).each do |attribute_type|
+ assert_field_type attribute_type, :text_field
+ end
+ end
+
+ def test_field_type_returns_datetime_select
+ %w(datetime timestamp).each do |attribute_type|
+ assert_field_type attribute_type, :datetime_select
+ end
+ end
+
+ def test_field_type_returns_time_select
+ assert_field_type :time, :time_select
+ end
+
+ def test_field_type_returns_date_select
+ assert_field_type :date, :date_select
+ end
+
+ def test_field_type_returns_text_area
+ assert_field_type :text, :text_area
+ end
+
+ def test_field_type_returns_check_box
+ assert_field_type :boolean, :check_box
+ end
+
+ def test_field_type_with_unknown_type_returns_text_field
+ %w(foo bar baz).each do |attribute_type|
+ assert_field_type attribute_type, :text_field
+ end
+ end
+
+ def test_default_value_is_integer
+ assert_field_default_value :integer, 1
+ end
+
+ def test_default_value_is_float
+ assert_field_default_value :float, 1.5
+ end
+
+ def test_default_value_is_decimal
+ assert_field_default_value :decimal, '9.99'
+ end
+
+ def test_default_value_is_datetime
+ %w(datetime timestamp time).each do |attribute_type|
+ assert_field_default_value attribute_type, Time.now.to_s(:db)
+ end
+ end
+
+ def test_default_value_is_date
+ assert_field_default_value :date, Date.today.to_s(:db)
+ end
+
+ def test_default_value_is_string
+ assert_field_default_value :string, 'MyString'
+ end
+
+ def test_default_value_for_type
+ att = Rails::Generators::GeneratedAttribute.parse("type:string")
+ assert_equal("", att.default)
+ end
+
+ def test_default_value_is_text
+ assert_field_default_value :text, 'MyText'
+ end
+
+ def test_default_value_is_boolean
+ assert_field_default_value :boolean, false
+ end
+
+ def test_default_value_is_nil
+ %w(references belongs_to).each do |attribute_type|
+ assert_field_default_value attribute_type, nil
+ end
+ end
+
+ def test_default_value_is_empty_string
+ %w(foo bar baz).each do |attribute_type|
+ assert_field_default_value attribute_type, ''
+ end
+ end
+
+ def test_human_name
+ assert_equal(
+ 'Full name',
+ create_generated_attribute(:string, 'full_name').human_name
+ )
+ end
+
+ def test_reference_is_true
+ %w(references belongs_to).each do |attribute_type|
+ assert create_generated_attribute(attribute_type).reference?
+ end
+ end
+
+ def test_reference_is_false
+ %w(foo bar baz).each do |attribute_type|
+ assert !create_generated_attribute(attribute_type).reference?
+ end
+ end
+
+ def test_polymorphic_reference_is_true
+ %w(references belongs_to).each do |attribute_type|
+ assert create_generated_attribute("#{attribute_type}{polymorphic}").polymorphic?
+ end
+ end
+
+ def test_polymorphic_reference_is_false
+ %w(foo bar baz).each do |attribute_type|
+ assert !create_generated_attribute("#{attribute_type}{polymorphic}").polymorphic?
+ end
+ end
+
+ def test_blank_type_defaults_to_string_raises_exception
+ assert_equal :string, create_generated_attribute(nil, 'title').type
+ assert_equal :string, create_generated_attribute("", 'title').type
+ end
+
+ def test_handles_index_names_for_references
+ assert_equal "post", create_generated_attribute('string', 'post').index_name
+ assert_equal "post_id", create_generated_attribute('references', 'post').index_name
+ assert_equal "post_id", create_generated_attribute('belongs_to', 'post').index_name
+ assert_equal ["post_id", "post_type"], create_generated_attribute('references{polymorphic}', 'post').index_name
+ end
+
+ def test_handles_column_names_for_references
+ assert_equal "post", create_generated_attribute('string', 'post').column_name
+ assert_equal "post_id", create_generated_attribute('references', 'post').column_name
+ assert_equal "post_id", create_generated_attribute('belongs_to', 'post').column_name
+ end
+
+ def test_parse_required_attribute_with_index
+ att = Rails::Generators::GeneratedAttribute.parse("supplier:references{required}:index")
+ assert_equal "supplier", att.name
+ assert_equal :references, att.type
+ assert att.has_index?
+ assert att.required?
+ end
+end
diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb
new file mode 100644
index 0000000000..dcfeaaa8e0
--- /dev/null
+++ b/railties/test/generators/generator_generator_test.rb
@@ -0,0 +1,71 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/generator/generator_generator'
+
+class GeneratorGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(awesome)
+
+ def test_generator_skeleton_is_created
+ run_generator
+
+ %w(
+ lib/generators/awesome
+ lib/generators/awesome/USAGE
+ lib/generators/awesome/templates
+ ).each{ |path| assert_file path }
+
+ assert_file "lib/generators/awesome/awesome_generator.rb",
+ /class AwesomeGenerator < Rails::Generators::NamedBase/
+ assert_file "test/lib/generators/awesome_generator_test.rb",
+ /class AwesomeGeneratorTest < Rails::Generators::TestCase/,
+ /require 'generators\/awesome\/awesome_generator'/
+ end
+
+ def test_namespaced_generator_skeleton
+ run_generator ["rails/awesome"]
+
+ %w(
+ lib/generators/rails/awesome
+ lib/generators/rails/awesome/USAGE
+ lib/generators/rails/awesome/templates
+ ).each{ |path| assert_file path }
+
+ assert_file "lib/generators/rails/awesome/awesome_generator.rb",
+ /class Rails::AwesomeGenerator < Rails::Generators::NamedBase/
+ assert_file "test/lib/generators/rails/awesome_generator_test.rb",
+ /class Rails::AwesomeGeneratorTest < Rails::Generators::TestCase/,
+ /require 'generators\/rails\/awesome\/awesome_generator'/
+ end
+
+ def test_generator_skeleton_is_created_without_file_name_namespace
+ run_generator ["awesome", "--namespace", "false"]
+
+ %w(
+ lib/generators/
+ lib/generators/USAGE
+ lib/generators/templates
+ ).each{ |path| assert_file path }
+
+ assert_file "lib/generators/awesome_generator.rb",
+ /class AwesomeGenerator < Rails::Generators::NamedBase/
+ assert_file "test/lib/generators/awesome_generator_test.rb",
+ /class AwesomeGeneratorTest < Rails::Generators::TestCase/,
+ /require 'generators\/awesome_generator'/
+ end
+
+ def test_namespaced_generator_skeleton_without_file_name_namespace
+ run_generator ["rails/awesome", "--namespace", "false"]
+
+ %w(
+ lib/generators/rails
+ lib/generators/rails/USAGE
+ lib/generators/rails/templates
+ ).each{ |path| assert_file path }
+
+ assert_file "lib/generators/rails/awesome_generator.rb",
+ /class Rails::AwesomeGenerator < Rails::Generators::NamedBase/
+ assert_file "test/lib/generators/rails/awesome_generator_test.rb",
+ /class Rails::AwesomeGeneratorTest < Rails::Generators::TestCase/,
+ /require 'generators\/rails\/awesome_generator'/
+ end
+end
diff --git a/railties/test/generators/generator_test.rb b/railties/test/generators/generator_test.rb
new file mode 100644
index 0000000000..7871399dd7
--- /dev/null
+++ b/railties/test/generators/generator_test.rb
@@ -0,0 +1,85 @@
+require 'active_support/test_case'
+require 'active_support/testing/autorun'
+require 'rails/generators/app_base'
+
+module Rails
+ module Generators
+ class GeneratorTest < ActiveSupport::TestCase
+ def make_builder_class
+ Class.new(AppBase) do
+ add_shared_options_for "application"
+
+ # include a module to get around thor's method_added hook
+ include(Module.new {
+ def gemfile_entries; super; end
+ def invoke_all; super; self; end
+ def add_gem_entry_filter; super; end
+ def gemfile_entry(*args); super; end
+ })
+ end
+ end
+
+ def test_construction
+ klass = make_builder_class
+ assert klass.start(['new', 'blah'])
+ end
+
+ def test_add_gem
+ klass = make_builder_class
+ generator = klass.start(['new', 'blah'])
+ generator.gemfile_entry 'tenderlove'
+ assert_includes generator.gemfile_entries.map(&:name), 'tenderlove'
+ end
+
+ def test_add_gem_with_version
+ klass = make_builder_class
+ generator = klass.start(['new', 'blah'])
+ generator.gemfile_entry 'tenderlove', '2.0.0'
+ assert generator.gemfile_entries.find { |gfe|
+ gfe.name == 'tenderlove' && gfe.version == '2.0.0'
+ }
+ end
+
+ def test_add_github_gem
+ klass = make_builder_class
+ generator = klass.start(['new', 'blah'])
+ generator.gemfile_entry 'tenderlove', github: 'hello world'
+ assert generator.gemfile_entries.find { |gfe|
+ gfe.name == 'tenderlove' && gfe.options[:github] == 'hello world'
+ }
+ end
+
+ def test_add_path_gem
+ klass = make_builder_class
+ generator = klass.start(['new', 'blah'])
+ generator.gemfile_entry 'tenderlove', path: 'hello world'
+ assert generator.gemfile_entries.find { |gfe|
+ gfe.name == 'tenderlove' && gfe.options[:path] == 'hello world'
+ }
+ end
+
+ def test_filter
+ klass = make_builder_class
+ generator = klass.start(['new', 'blah'])
+ gems = generator.gemfile_entries
+ generator.add_gem_entry_filter { |gem|
+ gem.name != gems.first.name
+ }
+ assert_equal gems.drop(1), generator.gemfile_entries
+ end
+
+ def test_two_filters
+ klass = make_builder_class
+ generator = klass.start(['new', 'blah'])
+ gems = generator.gemfile_entries
+ generator.add_gem_entry_filter { |gem|
+ gem.name != gems.first.name
+ }
+ generator.add_gem_entry_filter { |gem|
+ gem.name != gems[1].name
+ }
+ assert_equal gems.drop(2), generator.gemfile_entries
+ end
+ end
+ end
+end
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
new file mode 100644
index 0000000000..b19a5a7144
--- /dev/null
+++ b/railties/test/generators/generators_test_helper.rb
@@ -0,0 +1,50 @@
+require 'abstract_unit'
+require 'active_support/core_ext/module/remove_method'
+require 'active_support/testing/stream'
+require 'active_support/testing/method_call_assertions'
+require 'rails/generators'
+require 'rails/generators/test_case'
+
+module Rails
+ class << self
+ remove_possible_method :root
+ def root
+ @root ||= Pathname.new(File.expand_path('../../fixtures', __FILE__))
+ end
+ end
+end
+Rails.application.config.root = Rails.root
+Rails.application.config.generators.templates = [File.join(Rails.root, "lib", "templates")]
+
+# Call configure to load the settings from
+# Rails.application.config.generators to Rails::Generators
+Rails.application.load_generators
+
+require 'active_record'
+require 'action_dispatch'
+require 'action_view'
+
+module GeneratorsTestHelper
+ include ActiveSupport::Testing::Stream
+ include ActiveSupport::Testing::MethodCallAssertions
+
+ def self.included(base)
+ base.class_eval do
+ destination File.join(Rails.root, "tmp")
+ setup :prepare_destination
+
+ begin
+ base.tests Rails::Generators.const_get(base.name.sub(/Test$/, ''))
+ rescue
+ end
+ end
+ end
+
+ def copy_routes
+ routes = File.expand_path("../../../lib/rails/generators/rails/app/templates/config/routes.rb", __FILE__)
+ destination = File.join(destination_root, "config")
+ FileUtils.mkdir_p(destination)
+ FileUtils.cp routes, destination
+ end
+
+end
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
new file mode 100644
index 0000000000..add04f21a4
--- /dev/null
+++ b/railties/test/generators/helper_generator_test.rb
@@ -0,0 +1,39 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/helper/helper_generator'
+
+ObjectHelper = Class.new
+AnotherObjectHelperTest = Class.new
+
+class HelperGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(admin)
+
+ def test_helper_skeleton_is_created
+ run_generator
+ assert_file "app/helpers/admin_helper.rb", /module AdminHelper/
+ end
+
+ def test_check_class_collision
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match(/The name 'ObjectHelper' is either already used in your application or reserved/, content)
+ end
+
+ def test_namespaced_and_not_namespaced_helpers
+ run_generator ["products"]
+
+ # We have to require the generated helper to show the problem because
+ # the test helpers just check for generated files and contents but
+ # do not actually load them. But they have to be loaded (as in a real environment)
+ # to make the second generator run fail
+ require "#{destination_root}/app/helpers/products_helper"
+
+ assert_nothing_raised do
+ begin
+ run_generator ["admin::products"]
+ ensure
+ # cleanup
+ Object.send(:remove_const, :ProductsHelper)
+ end
+ end
+ end
+end
diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb
new file mode 100644
index 0000000000..d05ed76d24
--- /dev/null
+++ b/railties/test/generators/integration_test_generator_test.rb
@@ -0,0 +1,12 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/integration_test/integration_test_generator'
+
+class IntegrationTestGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(integration)
+
+ def test_integration_test_skeleton_is_created
+ run_generator
+ assert_file "test/integration/integration_test.rb", /class IntegrationTest < ActionDispatch::IntegrationTest/
+ end
+end
diff --git a/railties/test/generators/job_generator_test.rb b/railties/test/generators/job_generator_test.rb
new file mode 100644
index 0000000000..7fd8f2062f
--- /dev/null
+++ b/railties/test/generators/job_generator_test.rb
@@ -0,0 +1,29 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/job/job_generator'
+
+class JobGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ def test_job_skeleton_is_created
+ run_generator ["refresh_counters"]
+ assert_file "app/jobs/refresh_counters_job.rb" do |job|
+ assert_match(/class RefreshCountersJob < ApplicationJob/, job)
+ end
+ end
+
+ def test_job_queue_param
+ run_generator ["refresh_counters", "--queue", "important"]
+ assert_file "app/jobs/refresh_counters_job.rb" do |job|
+ assert_match(/class RefreshCountersJob < ApplicationJob/, job)
+ assert_match(/queue_as :important/, job)
+ end
+ end
+
+ def test_job_namespace
+ run_generator ["admin/refresh_counters", "--queue", "admin"]
+ assert_file "app/jobs/admin/refresh_counters_job.rb" do |job|
+ assert_match(/class Admin::RefreshCountersJob < ApplicationJob/, job)
+ assert_match(/queue_as :admin/, job)
+ end
+ end
+end
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
new file mode 100644
index 0000000000..f01e8cd2d9
--- /dev/null
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -0,0 +1,186 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/mailer/mailer_generator'
+
+class MailerGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(notifier foo bar)
+
+ def test_mailer_skeleton_is_created
+ run_generator
+ assert_file "app/mailers/notifier_mailer.rb" do |mailer|
+ assert_match(/class NotifierMailer < ApplicationMailer/, mailer)
+ assert_no_match(/default from: "from@example.com"/, mailer)
+ assert_no_match(/layout :mailer_notifier/, mailer)
+ end
+ end
+
+ def test_application_mailer_skeleton_is_created
+ run_generator
+ assert_file "app/mailers/application_mailer.rb" do |mailer|
+ assert_match(/class ApplicationMailer < ActionMailer::Base/, mailer)
+ assert_match(/default from: "from@example.com"/, mailer)
+ assert_match(/layout 'mailer'/, mailer)
+ end
+ end
+
+ def test_mailer_with_i18n_helper
+ run_generator
+ assert_file "app/mailers/notifier_mailer.rb" do |mailer|
+ assert_match(/en\.notifier_mailer\.foo\.subject/, mailer)
+ assert_match(/en\.notifier_mailer\.bar\.subject/, mailer)
+ end
+ end
+
+ def test_check_class_collision
+ Object.send :const_set, :NotifierMailer, Class.new
+ content = capture(:stderr){ run_generator }
+ assert_match(/The name 'NotifierMailer' is either already used in your application or reserved/, content)
+ ensure
+ Object.send :remove_const, :NotifierMailer
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/mailers/notifier_mailer_test.rb" do |test|
+ assert_match(/class NotifierMailerTest < ActionMailer::TestCase/, test)
+ assert_match(/test "foo"/, test)
+ assert_match(/test "bar"/, test)
+ end
+ assert_file "test/mailers/previews/notifier_mailer_preview.rb" do |preview|
+ assert_match(/\# Preview all emails at http:\/\/localhost\:3000\/rails\/mailers\/notifier_mailer/, preview)
+ assert_match(/class NotifierMailerPreview < ActionMailer::Preview/, preview)
+ assert_match(/\# Preview this email at http:\/\/localhost\:3000\/rails\/mailers\/notifier_mailer\/foo/, preview)
+ assert_instance_method :foo, preview do |foo|
+ assert_match(/NotifierMailer.foo/, foo)
+ end
+ assert_match(/\# Preview this email at http:\/\/localhost\:3000\/rails\/mailers\/notifier_mailer\/bar/, preview)
+ assert_instance_method :bar, preview do |bar|
+ assert_match(/NotifierMailer.bar/, bar)
+ end
+ end
+ end
+
+ def test_check_test_class_collision
+ Object.send :const_set, :NotifierMailerTest, Class.new
+ content = capture(:stderr){ run_generator }
+ assert_match(/The name 'NotifierMailerTest' is either already used in your application or reserved/, content)
+ ensure
+ Object.send :remove_const, :NotifierMailerTest
+ end
+
+ def test_check_preview_class_collision
+ Object.send :const_set, :NotifierMailerPreview, Class.new
+ content = capture(:stderr){ run_generator }
+ assert_match(/The name 'NotifierMailerPreview' is either already used in your application or reserved/, content)
+ ensure
+ Object.send :remove_const, :NotifierMailerPreview
+ end
+
+ def test_invokes_default_text_template_engine
+ run_generator
+ assert_file "app/views/notifier_mailer/foo.text.erb" do |view|
+ assert_match(%r(\sapp/views/notifier_mailer/foo\.text\.erb), view)
+ assert_match(/<%= @greeting %>/, view)
+ end
+
+ assert_file "app/views/notifier_mailer/bar.text.erb" do |view|
+ assert_match(%r(\sapp/views/notifier_mailer/bar\.text\.erb), view)
+ assert_match(/<%= @greeting %>/, view)
+ end
+
+ assert_file "app/views/layouts/mailer.text.erb" do |view|
+ assert_match(/<%= yield %>/, view)
+ end
+ end
+
+ def test_invokes_default_html_template_engine
+ run_generator
+ assert_file "app/views/notifier_mailer/foo.html.erb" do |view|
+ assert_match(%r(\sapp/views/notifier_mailer/foo\.html\.erb), view)
+ assert_match(/<%= @greeting %>/, view)
+ end
+
+ assert_file "app/views/notifier_mailer/bar.html.erb" do |view|
+ assert_match(%r(\sapp/views/notifier_mailer/bar\.html\.erb), view)
+ assert_match(/<%= @greeting %>/, view)
+ end
+
+ assert_file "app/views/layouts/mailer.html.erb" do |view|
+ assert_match(%r{<html>\n <body>\n <%= yield %>\n </body>\n</html>}, view)
+ end
+ end
+
+ def test_invokes_default_template_engine_even_with_no_action
+ run_generator ["notifier"]
+ assert_file "app/views/notifier_mailer"
+ assert_file "app/views/layouts/mailer.text.erb"
+ assert_file "app/views/layouts/mailer.html.erb"
+ end
+
+ def test_logs_if_the_template_engine_cannot_be_found
+ content = run_generator ["notifier", "foo", "bar", "--template-engine=haml"]
+ assert_match(/haml \[not found\]/, content)
+ end
+
+ def test_mailer_with_namedspaced_mailer
+ run_generator ["Farm::Animal", "moos"]
+ assert_file "app/mailers/farm/animal_mailer.rb" do |mailer|
+ assert_match(/class Farm::AnimalMailer < ApplicationMailer/, mailer)
+ assert_match(/en\.farm\.animal_mailer\.moos\.subject/, mailer)
+ end
+ assert_file "test/mailers/previews/farm/animal_mailer_preview.rb" do |preview|
+ assert_match(/\# Preview all emails at http:\/\/localhost\:3000\/rails\/mailers\/farm\/animal_mailer/, preview)
+ assert_match(/class Farm::AnimalMailerPreview < ActionMailer::Preview/, preview)
+ assert_match(/\# Preview this email at http:\/\/localhost\:3000\/rails\/mailers\/farm\/animal_mailer\/moos/, preview)
+ end
+ assert_file "app/views/farm/animal_mailer/moos.text.erb"
+ assert_file "app/views/farm/animal_mailer/moos.html.erb"
+ end
+
+ def test_actions_are_turned_into_methods
+ run_generator
+
+ assert_file "app/mailers/notifier_mailer.rb" do |mailer|
+ assert_instance_method :foo, mailer do |foo|
+ assert_match(/mail to: "to@example.org"/, foo)
+ assert_match(/@greeting = "Hi"/, foo)
+ end
+
+ assert_instance_method :bar, mailer do |bar|
+ assert_match(/mail to: "to@example.org"/, bar)
+ assert_match(/@greeting = "Hi"/, bar)
+ end
+ end
+ end
+
+ def test_mailer_on_revoke
+ run_generator
+ run_generator ["notifier"], behavior: :revoke
+
+ assert_no_file "app/mailers/notifier.rb"
+ assert_no_file "app/views/notifier/foo.text.erb"
+ assert_no_file "app/views/notifier/bar.text.erb"
+ assert_no_file "app/views/notifier/foo.html.erb"
+ assert_no_file "app/views/notifier/bar.html.erb"
+
+ assert_file "app/mailers/application_mailer.rb"
+ assert_file "app/views/layouts/mailer.text.erb"
+ assert_file "app/views/layouts/mailer.html.erb"
+ end
+
+ def test_mailer_suffix_is_not_duplicated
+ run_generator ["notifier_mailer"]
+
+ assert_no_file "app/mailers/notifier_mailer_mailer.rb"
+ assert_file "app/mailers/notifier_mailer.rb"
+
+ assert_no_file "app/views/notifier_mailer_mailer/"
+ assert_file "app/views/notifier_mailer/"
+
+ assert_no_file "test/mailers/notifier_mailer_mailer_test.rb"
+ assert_file "test/mailers/notifier_mailer_test.rb"
+
+ assert_no_file "test/mailers/previews/notifier_mailer_mailer_preview.rb"
+ assert_file "test/mailers/previews/notifier_mailer_preview.rb"
+ end
+end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
new file mode 100644
index 0000000000..199743a396
--- /dev/null
+++ b/railties/test/generators/migration_generator_test.rb
@@ -0,0 +1,321 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/migration/migration_generator'
+
+class MigrationGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ def test_migration
+ migration = "change_title_body_from_posts"
+ run_generator [migration]
+ assert_migration "db/migrate/#{migration}.rb", /class ChangeTitleBodyFromPosts < ActiveRecord::Migration/
+ end
+
+ def test_migrations_generated_simultaneously
+ migrations = ["change_title_body_from_posts", "change_email_from_comments"]
+
+ first_migration_number, second_migration_number = migrations.collect do |migration|
+ run_generator [migration]
+ file_name = migration_file_name "db/migrate/#{migration}.rb"
+
+ File.basename(file_name).split('_').first
+ end
+
+ assert_not_equal first_migration_number, second_migration_number
+ end
+
+ def test_migration_with_class_name
+ migration = "ChangeTitleBodyFromPosts"
+ run_generator [migration]
+ assert_migration "db/migrate/change_title_body_from_posts.rb", /class #{migration} < ActiveRecord::Migration/
+ end
+
+ def test_migration_with_invalid_file_name
+ migration = "add_something:datetime"
+ assert_raise ActiveRecord::IllegalMigrationNameError do
+ run_generator [migration]
+ end
+ end
+
+ def test_add_migration_with_attributes
+ migration = "add_title_body_to_posts"
+ run_generator [migration, "title:string", "body:text"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :posts, :title, :string/, change)
+ assert_match(/add_column :posts, :body, :text/, change)
+ end
+ end
+ end
+
+ def test_remove_migration_with_indexed_attribute
+ migration = "remove_title_body_from_posts"
+ run_generator [migration, "title:string:index", "body:text"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/remove_column :posts, :title, :string/, change)
+ assert_match(/remove_column :posts, :body, :text/, change)
+ assert_match(/remove_index :posts, :title/, change)
+ end
+ end
+ end
+
+ def test_remove_migration_with_attributes
+ migration = "remove_title_body_from_posts"
+ run_generator [migration, "title:string", "body:text"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/remove_column :posts, :title, :string/, change)
+ assert_match(/remove_column :posts, :body, :text/, change)
+ end
+ end
+ end
+
+ def test_remove_migration_with_references_options
+ migration = "remove_references_from_books"
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/remove_reference :books, :author, index: true/, change)
+ assert_match(/remove_reference :books, :distributor, polymorphic: true, index: true/, change)
+ end
+ end
+ end
+
+ def test_remove_migration_with_references_removes_foreign_keys
+ migration = "remove_references_from_books"
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/remove_reference :books, :author,.*\sforeign_key: true/, change)
+ assert_match(/remove_reference :books, :distributor/, change) # sanity check
+ assert_no_match(/remove_reference :books, :distributor,.*\sforeign_key: true/, change)
+ end
+ end
+ end
+
+ def test_add_migration_with_attributes_and_indices
+ migration = "add_title_with_index_and_body_to_posts"
+ run_generator [migration, "title:string:index", "body:text", "user_id:integer:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :posts, :title, :string/, change)
+ assert_match(/add_column :posts, :body, :text/, change)
+ assert_match(/add_column :posts, :user_id, :integer/, change)
+ assert_match(/add_index :posts, :title/, change)
+ assert_match(/add_index :posts, :user_id, unique: true/, change)
+ end
+ end
+ end
+
+ def test_add_migration_with_attributes_and_wrong_index_declaration
+ migration = "add_title_and_content_to_books"
+ run_generator [migration, "title:string:inex", "content:text", "user_id:integer:unik"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :books, :title, :string/, change)
+ assert_match(/add_column :books, :content, :text/, change)
+ assert_match(/add_column :books, :user_id, :integer/, change)
+ end
+ assert_no_match(/add_index :books, :title/, content)
+ assert_no_match(/add_index :books, :user_id/, content)
+ end
+ end
+
+ def test_add_migration_with_attributes_without_type_and_index
+ migration = "add_title_with_index_and_body_to_posts"
+ run_generator [migration, "title:index", "body:text", "user_uuid:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :posts, :title, :string/, change)
+ assert_match(/add_column :posts, :body, :text/, change)
+ assert_match(/add_column :posts, :user_uuid, :string/, change)
+ assert_match(/add_index :posts, :title/, change)
+ assert_match(/add_index :posts, :user_uuid, unique: true/, change)
+ end
+ end
+ end
+
+ def test_add_migration_with_attributes_index_declaration_and_attribute_options
+ migration = "add_title_and_content_to_books"
+ run_generator [migration, "title:string{40}:index", "content:string{255}", "price:decimal{1,2}:index", "discount:decimal{3.4}:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :books, :title, :string, limit: 40/, change)
+ assert_match(/add_column :books, :content, :string, limit: 255/, change)
+ assert_match(/add_column :books, :price, :decimal, precision: 1, scale: 2/, change)
+ assert_match(/add_column :books, :discount, :decimal, precision: 3, scale: 4/, change)
+ end
+ assert_match(/add_index :books, :title/, content)
+ assert_match(/add_index :books, :price/, content)
+ assert_match(/add_index :books, :discount, unique: true/, content)
+ end
+ end
+
+ def test_add_migration_with_references_options
+ migration = "add_references_to_books"
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_reference :books, :author, index: true/, change)
+ assert_match(/add_reference :books, :distributor, polymorphic: true, index: true/, change)
+ end
+ end
+ end
+
+ def test_add_migration_with_required_references
+ migration = "add_references_to_books"
+ run_generator [migration, "author:belongs_to{required}", "distributor:references{polymorphic,required}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_reference :books, :author, index: true, null: false/, change)
+ assert_match(/add_reference :books, :distributor, polymorphic: true, index: true, null: false/, change)
+ end
+ end
+ end
+
+ def test_add_migration_with_references_adds_foreign_keys
+ migration = "add_references_to_books"
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_reference :books, :author,.*\sforeign_key: true/, change)
+ assert_match(/add_reference :books, :distributor/, change) # sanity check
+ assert_no_match(/add_reference :books, :distributor,.*\sforeign_key: true/, change)
+ end
+ end
+ end
+
+ def test_create_join_table_migration
+ migration = "add_media_join_table"
+ run_generator [migration, "artist_id", "musics:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_join_table :artists, :musics/, change)
+ assert_match(/# t.index \[:artist_id, :music_id\]/, change)
+ assert_match(/ t.index \[:music_id, :artist_id\], unique: true/, change)
+ end
+ end
+ end
+
+ def test_create_table_migration
+ run_generator ["create_books", "title:string", "content:text"]
+ assert_migration "db/migrate/create_books.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :books/, change)
+ assert_match(/ t\.string :title/, change)
+ assert_match(/ t\.text :content/, change)
+ end
+ end
+ end
+
+ def test_add_uuid_to_create_table_migration
+ run_generator ["create_books", "--primary_key_type=uuid"]
+ assert_migration "db/migrate/create_books.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :books, id: :uuid/, change)
+ end
+ end
+ end
+
+ def test_should_create_empty_migrations_if_name_not_start_with_add_or_remove_or_create
+ migration = "delete_books"
+ run_generator [migration, "title:string", "content:text"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/^\s*$/, change)
+ end
+ end
+ end
+
+ def test_properly_identifies_usage_file
+ assert generator_class.send(:usage_path)
+ end
+
+ def test_migration_with_singular_table_name
+ with_singular_table_name do
+ migration = "add_title_body_to_post"
+ run_generator [migration, 'title:string']
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :post, :title, :string/, change)
+ end
+ end
+ end
+ end
+
+ def test_create_join_table_migration_with_singular_table_name
+ with_singular_table_name do
+ migration = "add_media_join_table"
+ run_generator [migration, "artist_id", "music:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_join_table :artist, :music/, change)
+ assert_match(/# t.index \[:artist_id, :music_id\]/, change)
+ assert_match(/ t.index \[:music_id, :artist_id\], unique: true/, change)
+ end
+ end
+ end
+ end
+
+ def test_create_table_migration_with_singular_table_name
+ with_singular_table_name do
+ run_generator ["create_book", "title:string", "content:text"]
+ assert_migration "db/migrate/create_book.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :book/, change)
+ assert_match(/ t\.string :title/, change)
+ assert_match(/ t\.text :content/, change)
+ end
+ end
+ end
+ end
+
+ def test_create_table_migration_with_token_option
+ run_generator ["create_users", "token:token", "auth_token:token"]
+ assert_migration "db/migrate/create_users.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :users/, change)
+ assert_match(/ t\.string :token/, change)
+ assert_match(/ t\.string :auth_token/, change)
+ assert_match(/add_index :users, :token, unique: true/, change)
+ assert_match(/add_index :users, :auth_token, unique: true/, change)
+ end
+ end
+ end
+
+ def test_add_migration_with_token_option
+ migration = "add_token_to_users"
+ run_generator [migration, "auth_token:token"]
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :users, :auth_token, :string/, change)
+ assert_match(/add_index :users, :auth_token, unique: true/, change)
+ end
+ end
+ end
+
+ private
+
+ def with_singular_table_name
+ old_state = ActiveRecord::Base.pluralize_table_names
+ ActiveRecord::Base.pluralize_table_names = false
+ yield
+ ensure
+ ActiveRecord::Base.pluralize_table_names = old_state
+ end
+end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
new file mode 100644
index 0000000000..64b9a480f3
--- /dev/null
+++ b/railties/test/generators/model_generator_test.rb
@@ -0,0 +1,476 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/model/model_generator'
+require 'active_support/core_ext/string/strip'
+
+class ModelGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(Account name:string age:integer)
+
+ def test_help_shows_invoked_generators_options
+ content = run_generator ["--help"]
+ assert_match(/ActiveRecord options:/, content)
+ assert_match(/TestUnit options:/, content)
+ end
+
+ def test_model_with_missing_attribute_type
+ run_generator ["post", "title", "body:text", "author"]
+
+ assert_migration "db/migrate/create_posts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.string :title/, up)
+ assert_match(/t\.text :body/, up)
+ assert_match(/t\.string :author/, up)
+ end
+ end
+ end
+
+ def test_invokes_default_orm
+ run_generator
+ assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/
+ end
+
+ def test_model_with_parent_option
+ run_generator ["account", "--parent", "Admin::Account"]
+ assert_file "app/models/account.rb", /class Account < Admin::Account/
+ assert_no_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_plural_names_are_singularized
+ content = run_generator ["accounts".freeze]
+ assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/
+ assert_file "test/models/account_test.rb", /class AccountTest/
+ assert_match(/\[WARNING\] The model name 'accounts' was recognized as a plural, using the singular 'account' instead\. Override with --force-plural or setup custom inflection rules for this noun before running the generator\./, content)
+ end
+
+ def test_model_with_underscored_parent_option
+ run_generator ["account", "--parent", "admin/account"]
+ assert_file "app/models/account.rb", /class Account < Admin::Account/
+ end
+
+ def test_model_with_namespace
+ run_generator ["admin/account"]
+ assert_file "app/models/admin.rb", /module Admin/
+ assert_file "app/models/admin.rb", /def self\.table_name_prefix/
+ assert_file "app/models/admin.rb", /'admin_'/
+ assert_file "app/models/admin/account.rb", /class Admin::Account < ActiveRecord::Base/
+ end
+
+ def test_migration
+ run_generator
+ assert_migration "db/migrate/create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/
+ end
+
+ def test_migration_with_namespace
+ run_generator ["Gallery::Image"]
+ assert_migration "db/migrate/create_gallery_images", /class CreateGalleryImages < ActiveRecord::Migration/
+ assert_no_migration "db/migrate/create_images"
+ end
+
+ def test_migration_with_nested_namespace
+ run_generator ["Admin::Gallery::Image"]
+ assert_no_migration "db/migrate/create_images"
+ assert_no_migration "db/migrate/create_gallery_images"
+ assert_migration "db/migrate/create_admin_gallery_images", /class CreateAdminGalleryImages < ActiveRecord::Migration/
+ assert_migration "db/migrate/create_admin_gallery_images", /create_table :admin_gallery_images/
+ end
+
+ def test_migration_with_nested_namespace_without_pluralization
+ ActiveRecord::Base.pluralize_table_names = false
+ run_generator ["Admin::Gallery::Image"]
+ assert_no_migration "db/migrate/create_images"
+ assert_no_migration "db/migrate/create_gallery_images"
+ assert_no_migration "db/migrate/create_admin_gallery_images"
+ assert_migration "db/migrate/create_admin_gallery_image", /class CreateAdminGalleryImage < ActiveRecord::Migration/
+ assert_migration "db/migrate/create_admin_gallery_image", /create_table :admin_gallery_image/
+ ensure
+ ActiveRecord::Base.pluralize_table_names = true
+ end
+
+ def test_migration_with_namespaces_in_model_name_without_plurization
+ ActiveRecord::Base.pluralize_table_names = false
+ run_generator ["Gallery::Image"]
+ assert_migration "db/migrate/create_gallery_image", /class CreateGalleryImage < ActiveRecord::Migration/
+ assert_no_migration "db/migrate/create_gallery_images"
+ ensure
+ ActiveRecord::Base.pluralize_table_names = true
+ end
+
+ def test_migration_without_pluralization
+ ActiveRecord::Base.pluralize_table_names = false
+ run_generator
+ assert_migration "db/migrate/create_account", /class CreateAccount < ActiveRecord::Migration/
+ assert_no_migration "db/migrate/create_accounts"
+ ensure
+ ActiveRecord::Base.pluralize_table_names = true
+ end
+
+ def test_migration_is_skipped
+ run_generator ["account", "--no-migration"]
+ assert_no_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_migration_with_attributes
+ run_generator ["product", "name:string", "supplier_id:integer"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.integer :supplier_id/, up)
+ end
+ end
+ end
+
+ def test_migration_with_attributes_and_with_index
+ run_generator ["product", "name:string:index", "supplier_id:integer:index", "user_id:integer:uniq", "order_id:uniq"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.integer :supplier_id/, up)
+ assert_match(/t\.integer :user_id/, up)
+ assert_match(/t\.string :order_id/, up)
+
+ assert_match(/add_index :products, :name/, up)
+ assert_match(/add_index :products, :supplier_id/, up)
+ assert_match(/add_index :products, :user_id, unique: true/, up)
+ assert_match(/add_index :products, :order_id, unique: true/, up)
+ end
+ end
+ end
+
+ def test_migration_with_attributes_and_with_wrong_index_declaration
+ run_generator ["product", "name:string", "supplier_id:integer:inex", "user_id:integer:unqu"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.integer :supplier_id/, up)
+ assert_match(/t\.integer :user_id/, up)
+
+ assert_no_match(/add_index :products, :name/, up)
+ assert_no_match(/add_index :products, :supplier_id/, up)
+ assert_no_match(/add_index :products, :user_id/, up)
+ end
+ end
+ end
+
+ def test_migration_with_missing_attribute_type_and_with_index
+ run_generator ["product", "name:index", "supplier_id:integer:index", "year:integer"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.integer :supplier_id/, up)
+
+ assert_match(/add_index :products, :name/, up)
+ assert_match(/add_index :products, :supplier_id/, up)
+ assert_no_match(/add_index :products, :year/, up)
+ end
+ end
+ end
+
+ def test_add_migration_with_attributes_index_declaration_and_attribute_options
+ run_generator ["product", "title:string{40}:index", "content:string{255}", "price:decimal{5,2}:index", "discount:decimal{5,2}:uniq", "supplier:references{polymorphic}"]
+
+ assert_migration "db/migrate/create_products.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t.string :title, limit: 40/, up)
+ assert_match(/t.string :content, limit: 255/, up)
+ assert_match(/t.decimal :price, precision: 5, scale: 2/, up)
+ assert_match(/t.references :supplier, polymorphic: true/, up)
+ end
+ assert_match(/add_index :products, :title/, content)
+ assert_match(/add_index :products, :price/, content)
+ assert_match(/add_index :products, :discount, unique: true/, content)
+ end
+ end
+
+ def test_migration_without_timestamps
+ ActiveRecord::Base.timestamped_migrations = false
+ run_generator ["account"]
+ assert_file "db/migrate/001_create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/
+
+ run_generator ["project"]
+ assert_file "db/migrate/002_create_projects.rb", /class CreateProjects < ActiveRecord::Migration/
+ ensure
+ ActiveRecord::Base.timestamped_migrations = true
+ end
+
+ def test_model_with_references_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier:references"]
+ assert_file "app/models/product.rb", /belongs_to :supplier/
+ end
+
+ def test_model_with_belongs_to_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier:belongs_to"]
+ assert_file "app/models/product.rb", /belongs_to :supplier/
+ end
+
+ def test_model_with_polymorphic_references_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier:references{polymorphic}"]
+ assert_file "app/models/product.rb", /belongs_to :supplier, polymorphic: true/
+ end
+
+ def test_model_with_polymorphic_belongs_to_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier:belongs_to{polymorphic}"]
+ assert_file "app/models/product.rb", /belongs_to :supplier, polymorphic: true/
+ end
+
+ def test_migration_with_timestamps
+ run_generator
+ assert_migration "db/migrate/create_accounts.rb", /t.timestamps/
+ end
+
+ def test_migration_timestamps_are_skipped
+ run_generator ["account", "--no-timestamps"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_no_match(/t.timestamps/, up)
+ end
+ end
+ end
+
+ def test_migration_is_skipped_with_skip_option
+ run_generator
+ output = run_generator ["Account", "--skip"]
+ assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output
+ end
+
+ def test_migration_is_ignored_as_identical_with_skip_option
+ run_generator ["Account"]
+ output = run_generator ["Account", "--skip"]
+ assert_match %r{identical\s+db/migrate/\d+_create_accounts.rb}, output
+ end
+
+ def test_migration_is_skipped_on_skip_behavior
+ run_generator
+ output = run_generator ["Account"], behavior: :skip
+ assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output
+ end
+
+ def test_migration_error_is_not_shown_on_revoke
+ run_generator
+ error = capture(:stderr){ run_generator ["Account"], behavior: :revoke }
+ assert_no_match(/Another migration is already named create_accounts/, error)
+ end
+
+ def test_migration_is_removed_on_revoke
+ run_generator
+ run_generator ["Account"], behavior: :revoke
+ assert_no_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_existing_migration_is_removed_on_force
+ run_generator
+ old_migration = Dir["#{destination_root}/db/migrate/*_create_accounts.rb"].first
+ error = capture(:stderr) { run_generator ["Account", "--force"] }
+ assert_no_match(/Another migration is already named create_accounts/, error)
+ assert_no_file old_migration
+ assert_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/models/account_test.rb", /class AccountTest < ActiveSupport::TestCase/
+
+ assert_file "test/fixtures/accounts.yml", /name: MyString/, /age: 1/
+ assert_generated_fixture("test/fixtures/accounts.yml",
+ {"one"=>{"name"=>"MyString", "age"=>1}, "two"=>{"name"=>"MyString", "age"=>1}})
+ end
+
+ def test_fixtures_use_the_references_ids
+ run_generator ["LineItem", "product:references", "cart:belongs_to"]
+
+ assert_file "test/fixtures/line_items.yml", /product: \n cart: /
+ assert_generated_fixture("test/fixtures/line_items.yml",
+ {"one"=>{"product"=>nil, "cart"=>nil}, "two"=>{"product"=>nil, "cart"=>nil}})
+ end
+
+ def test_fixtures_use_the_references_ids_and_type
+ run_generator ["LineItem", "product:references{polymorphic}", "cart:belongs_to"]
+
+ assert_file "test/fixtures/line_items.yml", /product: \n product_type: Product\n cart: /
+ assert_generated_fixture("test/fixtures/line_items.yml",
+ {"one"=>{"product"=>nil, "product_type"=>"Product", "cart"=>nil},
+ "two"=>{"product"=>nil, "product_type"=>"Product", "cart"=>nil}})
+ end
+
+ def test_fixtures_respect_reserved_yml_keywords
+ run_generator ["LineItem", "no:integer", "Off:boolean", "ON:boolean"]
+
+ assert_generated_fixture("test/fixtures/line_items.yml",
+ {"one"=>{"no"=>1, "Off"=>false, "ON"=>false}, "two"=>{"no"=>1, "Off"=>false, "ON"=>false}})
+ end
+
+ def test_fixture_is_skipped
+ run_generator ["account", "--skip-fixture"]
+ assert_no_file "test/fixtures/accounts.yml"
+ end
+
+ def test_fixture_is_skipped_if_fixture_replacement_is_given
+ content = run_generator ["account", "-r", "factory_girl"]
+ assert_match(/factory_girl \[not found\]/, content)
+ assert_no_file "test/fixtures/accounts.yml"
+ end
+
+ def test_fixture_without_pluralization
+ original_pluralize_table_name = ActiveRecord::Base.pluralize_table_names
+ ActiveRecord::Base.pluralize_table_names = false
+ run_generator
+ assert_generated_fixture("test/fixtures/account.yml",
+ {"one"=>{"name"=>"MyString", "age"=>1}, "two"=>{"name"=>"MyString", "age"=>1}})
+ ensure
+ ActiveRecord::Base.pluralize_table_names = original_pluralize_table_name
+ end
+
+ def test_check_class_collision
+ content = capture(:stderr){ run_generator ["object"] }
+ assert_match(/The name 'Object' is either already used in your application or reserved/, content)
+ end
+
+ def test_index_is_added_for_belongs_to_association
+ run_generator ["account", "supplier:belongs_to"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/index: true/, up)
+ end
+ end
+ end
+
+ def test_index_is_added_for_references_association
+ run_generator ["account", "supplier:references"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/index: true/, up)
+ end
+ end
+ end
+
+ def test_index_is_skipped_for_belongs_to_association
+ run_generator ["account", "supplier:belongs_to", "--no-indexes"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_no_match(/index: true/, up)
+ end
+ end
+ end
+
+ def test_index_is_skipped_for_references_association
+ run_generator ["account", "supplier:references", "--no-indexes"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_no_match(/index: true/, up)
+ end
+ end
+ end
+
+ def test_add_uuid_to_create_table_migration
+ run_generator ["account", "--primary_key_type=uuid"]
+ assert_migration "db/migrate/create_accounts.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :accounts, id: :uuid/, change)
+ end
+ end
+ end
+
+ def test_required_belongs_to_adds_required_association
+ run_generator ["account", "supplier:references{required}"]
+
+ expected_file = <<-FILE.strip_heredoc
+ class Account < ActiveRecord::Base
+ belongs_to :supplier, required: true
+ end
+ FILE
+ assert_file "app/models/account.rb", expected_file
+ end
+
+ def test_required_polymorphic_belongs_to_generages_correct_model
+ run_generator ["account", "supplier:references{required,polymorphic}"]
+
+ expected_file = <<-FILE.strip_heredoc
+ class Account < ActiveRecord::Base
+ belongs_to :supplier, polymorphic: true, required: true
+ end
+ FILE
+ assert_file "app/models/account.rb", expected_file
+ end
+
+ def test_required_and_polymorphic_are_order_independent
+ run_generator ["account", "supplier:references{polymorphic.required}"]
+
+ expected_file = <<-FILE.strip_heredoc
+ class Account < ActiveRecord::Base
+ belongs_to :supplier, polymorphic: true, required: true
+ end
+ FILE
+ assert_file "app/models/account.rb", expected_file
+ end
+
+ def test_required_adds_null_false_to_column
+ run_generator ["account", "supplier:references{required}"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.references :supplier,.*\snull: false/, up)
+ end
+ end
+ end
+
+ def test_foreign_key_is_not_added_for_non_references
+ run_generator ["account", "supplier:string"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_no_match(/foreign_key/, up)
+ end
+ end
+ end
+
+ def test_foreign_key_is_added_for_references
+ run_generator ["account", "supplier:belongs_to", "user:references"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.belongs_to :supplier,.*\sforeign_key: true/, up)
+ assert_match(/t\.references :user,.*\sforeign_key: true/, up)
+ end
+ end
+ end
+
+ def test_foreign_key_is_skipped_for_polymorphic_references
+ run_generator ["account", "supplier:belongs_to{polymorphic}"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_no_match(/foreign_key/, up)
+ end
+ end
+ end
+
+ def test_token_option_adds_has_secure_token
+ run_generator ["user", "token:token", "auth_token:token"]
+ expected_file = <<-FILE.strip_heredoc
+ class User < ActiveRecord::Base
+ has_secure_token
+ has_secure_token :auth_token
+ end
+ FILE
+ assert_file "app/models/user.rb", expected_file
+ end
+
+ private
+ def assert_generated_fixture(path, parsed_contents)
+ fixture_file = File.new File.expand_path(path, destination_root)
+ assert_equal(parsed_contents, YAML.load(fixture_file))
+ end
+end
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
new file mode 100644
index 0000000000..291f5e06c3
--- /dev/null
+++ b/railties/test/generators/named_base_test.rb
@@ -0,0 +1,139 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
+
+class NamedBaseTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ tests Rails::Generators::ScaffoldControllerGenerator
+
+ def test_named_generator_with_underscore
+ g = generator ['line_item']
+ assert_name g, 'line_item', :name
+ assert_name g, %w(), :class_path
+ assert_name g, 'LineItem', :class_name
+ assert_name g, 'line_item', :file_path
+ assert_name g, 'line_item', :file_name
+ assert_name g, 'Line item', :human_name
+ assert_name g, 'line_item', :singular_name
+ assert_name g, 'line_items', :plural_name
+ assert_name g, 'line_item', :i18n_scope
+ assert_name g, 'line_items', :table_name
+ end
+
+ def test_named_generator_attributes
+ g = generator ['admin/foo']
+ assert_name g, 'admin/foo', :name
+ assert_name g, %w(admin), :class_path
+ assert_name g, 'Admin::Foo', :class_name
+ assert_name g, 'admin/foo', :file_path
+ assert_name g, 'foo', :file_name
+ assert_name g, 'Foo', :human_name
+ assert_name g, 'foo', :singular_name
+ assert_name g, 'foos', :plural_name
+ assert_name g, 'admin.foo', :i18n_scope
+ assert_name g, 'admin_foos', :table_name
+ end
+
+ def test_named_generator_attributes_as_ruby
+ g = generator ['Admin::Foo']
+ assert_name g, 'Admin::Foo', :name
+ assert_name g, %w(admin), :class_path
+ assert_name g, 'Admin::Foo', :class_name
+ assert_name g, 'admin/foo', :file_path
+ assert_name g, 'foo', :file_name
+ assert_name g, 'foo', :singular_name
+ assert_name g, 'Foo', :human_name
+ assert_name g, 'foos', :plural_name
+ assert_name g, 'admin.foo', :i18n_scope
+ assert_name g, 'admin_foos', :table_name
+ end
+
+ def test_named_generator_attributes_without_pluralized
+ original_pluralize_table_names = ActiveRecord::Base.pluralize_table_names
+ ActiveRecord::Base.pluralize_table_names = false
+
+ g = generator ['admin/foo']
+ assert_name g, 'admin_foo', :table_name
+ ensure
+ ActiveRecord::Base.pluralize_table_names = original_pluralize_table_names
+ end
+
+ def test_scaffold_plural_names
+ g = generator ['admin/foo']
+ assert_name g, 'admin/foos', :controller_name
+ assert_name g, %w(admin), :controller_class_path
+ assert_name g, 'Admin::Foos', :controller_class_name
+ assert_name g, 'admin/foos', :controller_file_path
+ assert_name g, 'foos', :controller_file_name
+ assert_name g, 'admin.foos', :controller_i18n_scope
+ end
+
+ def test_scaffold_plural_names_as_ruby
+ g = generator ['Admin::Foo']
+ assert_name g, 'Admin::Foos', :controller_name
+ assert_name g, %w(admin), :controller_class_path
+ assert_name g, 'Admin::Foos', :controller_class_name
+ assert_name g, 'admin/foos', :controller_file_path
+ assert_name g, 'foos', :controller_file_name
+ assert_name g, 'admin.foos', :controller_i18n_scope
+ end
+
+ def test_application_name
+ g = generator ['Admin::Foo']
+ Rails.stub(:application, Object.new) do
+ assert_name g, "object", :application_name
+ end
+
+ Rails.stub(:application, nil) do
+ assert_name g, "application", :application_name
+ end
+ end
+
+ def test_index_helper
+ g = generator ['Post']
+ assert_name g, 'posts', :index_helper
+ end
+
+ def test_index_helper_to_pluralize_once
+ g = generator ['Stadium']
+ assert_name g, 'stadia', :index_helper
+ end
+
+ def test_index_helper_with_uncountable
+ g = generator ['Sheep']
+ assert_name g, 'sheep_index', :index_helper
+ end
+
+ def test_hide_namespace
+ g = generator ['Hidden']
+ g.class.stub(:namespace, 'hidden') do
+ assert !Rails::Generators.hidden_namespaces.include?('hidden')
+ g.class.hide!
+ assert Rails::Generators.hidden_namespaces.include?('hidden')
+ end
+ end
+
+ def test_scaffold_plural_names_with_model_name_option
+ g = generator ['Admin::Foo'], model_name: 'User'
+ assert_name g, 'user', :singular_name
+ assert_name g, 'User', :name
+ assert_name g, 'user', :file_path
+ assert_name g, 'User', :class_name
+ assert_name g, 'user', :file_name
+ assert_name g, 'User', :human_name
+ assert_name g, 'users', :plural_name
+ assert_name g, 'user', :i18n_scope
+ assert_name g, 'users', :table_name
+ assert_name g, 'Admin::Foos', :controller_name
+ assert_name g, %w(admin), :controller_class_path
+ assert_name g, 'Admin::Foos', :controller_class_name
+ assert_name g, 'admin/foos', :controller_file_path
+ assert_name g, 'foos', :controller_file_name
+ assert_name g, 'admin.foos', :controller_i18n_scope
+ end
+
+ protected
+
+ def assert_name(generator, value, method)
+ assert_equal value, generator.send(method)
+ end
+end
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
new file mode 100644
index 0000000000..590f06e19a
--- /dev/null
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -0,0 +1,423 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/controller/controller_generator'
+require 'rails/generators/rails/model/model_generator'
+require 'rails/generators/mailer/mailer_generator'
+require 'rails/generators/rails/scaffold/scaffold_generator'
+
+class NamespacedGeneratorTestCase < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ def setup
+ super
+ Rails::Generators.namespace = TestApp
+ end
+end
+
+class NamespacedControllerGeneratorTest < NamespacedGeneratorTestCase
+ arguments %w(Account foo bar)
+ tests Rails::Generators::ControllerGenerator
+
+ setup :copy_routes
+
+ def test_namespaced_controller_skeleton_is_created
+ run_generator
+ assert_file "app/controllers/test_app/account_controller.rb",
+ /require_dependency "test_app\/application_controller"/,
+ /module TestApp/,
+ / class AccountController < ApplicationController/
+
+ assert_file "test/controllers/test_app/account_controller_test.rb",
+ /module TestApp/,
+ / class AccountControllerTest/
+ end
+
+ def test_skipping_namespace
+ run_generator ["Account", "--skip-namespace"]
+ assert_file "app/controllers/account_controller.rb", /class AccountController < ApplicationController/
+ assert_file "app/helpers/account_helper.rb", /module AccountHelper/
+ end
+
+ def test_namespaced_controller_with_additional_namespace
+ run_generator ["admin/account"]
+ assert_file "app/controllers/test_app/admin/account_controller.rb", /module TestApp/, / class Admin::AccountController < ApplicationController/ do |contents|
+ assert_match %r(require_dependency "test_app/application_controller"), contents
+ end
+ end
+
+ def test_helper_is_also_namespaced
+ run_generator
+ assert_file "app/helpers/test_app/account_helper.rb", /module TestApp/, / module AccountHelper/
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/controllers/test_app/account_controller_test.rb"
+ end
+
+ def test_invokes_default_template_engine
+ run_generator
+ assert_file "app/views/test_app/account/foo.html.erb", %r(app/views/test_app/account/foo\.html\.erb)
+ assert_file "app/views/test_app/account/bar.html.erb", %r(app/views/test_app/account/bar\.html\.erb)
+ end
+
+ def test_routes_should_not_be_namespaced
+ run_generator
+ assert_file "config/routes.rb", /get 'account\/foo'/, /get 'account\/bar'/
+ end
+
+ def test_invokes_default_template_engine_even_with_no_action
+ run_generator ["account"]
+ assert_file "app/views/test_app/account"
+ end
+
+ def test_namespaced_controller_dont_indent_blank_lines
+ run_generator
+ assert_file "app/controllers/test_app/account_controller.rb" do |content|
+ content.split("\n").each do |line|
+ assert_no_match(/^\s+$/, line, "Don't indent blank lines")
+ end
+ end
+ end
+end
+
+class NamespacedModelGeneratorTest < NamespacedGeneratorTestCase
+ arguments %w(Account name:string age:integer)
+ tests Rails::Generators::ModelGenerator
+
+ def test_module_file_is_not_created
+ run_generator
+ assert_no_file "app/models/test_app.rb"
+ end
+
+ def test_adds_namespace_to_model
+ run_generator
+ assert_file "app/models/test_app/account.rb", /module TestApp/, / class Account < ActiveRecord::Base/
+ end
+
+ def test_model_with_namespace
+ run_generator ["admin/account"]
+ assert_file "app/models/test_app/admin.rb", /module TestApp/, /module Admin/
+ assert_file "app/models/test_app/admin.rb", /def self\.table_name_prefix/
+ assert_file "app/models/test_app/admin.rb", /'test_app_admin_'/
+ assert_file "app/models/test_app/admin/account.rb", /module TestApp/, /class Admin::Account < ActiveRecord::Base/
+ end
+
+ def test_migration
+ run_generator
+ assert_migration "db/migrate/create_test_app_accounts.rb", /create_table :test_app_accounts/, /class CreateTestAppAccounts < ActiveRecord::Migration/
+ end
+
+ def test_migration_with_namespace
+ run_generator ["Gallery::Image"]
+ assert_migration "db/migrate/create_test_app_gallery_images", /class CreateTestAppGalleryImages < ActiveRecord::Migration/
+ assert_no_migration "db/migrate/create_test_app_images"
+ end
+
+ def test_migration_with_nested_namespace
+ run_generator ["Admin::Gallery::Image"]
+ assert_no_migration "db/migrate/create_images"
+ assert_no_migration "db/migrate/create_gallery_images"
+ assert_migration "db/migrate/create_test_app_admin_gallery_images", /class CreateTestAppAdminGalleryImages < ActiveRecord::Migration/
+ assert_migration "db/migrate/create_test_app_admin_gallery_images", /create_table :test_app_admin_gallery_images/
+ end
+
+ def test_migration_with_nested_namespace_without_pluralization
+ ActiveRecord::Base.pluralize_table_names = false
+ run_generator ["Admin::Gallery::Image"]
+ assert_no_migration "db/migrate/create_images"
+ assert_no_migration "db/migrate/create_gallery_images"
+ assert_no_migration "db/migrate/create_test_app_admin_gallery_images"
+ assert_migration "db/migrate/create_test_app_admin_gallery_image", /class CreateTestAppAdminGalleryImage < ActiveRecord::Migration/
+ assert_migration "db/migrate/create_test_app_admin_gallery_image", /create_table :test_app_admin_gallery_image/
+ ensure
+ ActiveRecord::Base.pluralize_table_names = true
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/models/test_app/account_test.rb", /module TestApp/, /class AccountTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/accounts.yml", /name: MyString/, /age: 1/
+ end
+end
+
+class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase
+ arguments %w(notifier foo bar)
+ tests Rails::Generators::MailerGenerator
+
+ def test_mailer_skeleton_is_created
+ run_generator
+ assert_file "app/mailers/test_app/notifier_mailer.rb" do |mailer|
+ assert_match(/module TestApp/, mailer)
+ assert_match(/class NotifierMailer < ApplicationMailer/, mailer)
+ assert_no_match(/default from: "from@example.com"/, mailer)
+ end
+ end
+
+ def test_mailer_with_i18n_helper
+ run_generator
+ assert_file "app/mailers/test_app/notifier_mailer.rb" do |mailer|
+ assert_match(/en\.notifier_mailer\.foo\.subject/, mailer)
+ assert_match(/en\.notifier_mailer\.bar\.subject/, mailer)
+ end
+ end
+
+ def test_invokes_default_test_framework
+ run_generator
+ assert_file "test/mailers/test_app/notifier_mailer_test.rb" do |test|
+ assert_match(/module TestApp/, test)
+ assert_match(/class NotifierMailerTest < ActionMailer::TestCase/, test)
+ assert_match(/test "foo"/, test)
+ assert_match(/test "bar"/, test)
+ end
+ end
+
+ def test_invokes_default_template_engine
+ run_generator
+ assert_file "app/views/test_app/notifier_mailer/foo.text.erb" do |view|
+ assert_match(%r(app/views/test_app/notifier_mailer/foo\.text\.erb), view)
+ assert_match(/<%= @greeting %>/, view)
+ end
+
+ assert_file "app/views/test_app/notifier_mailer/bar.text.erb" do |view|
+ assert_match(%r(app/views/test_app/notifier_mailer/bar\.text\.erb), view)
+ assert_match(/<%= @greeting %>/, view)
+ end
+ end
+
+ def test_invokes_default_template_engine_even_with_no_action
+ run_generator ["notifier"]
+ assert_file "app/views/test_app/notifier_mailer"
+ end
+end
+
+class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
+ include GeneratorsTestHelper
+ arguments %w(product_line title:string price:integer)
+ tests Rails::Generators::ScaffoldGenerator
+
+ setup :copy_routes
+
+ def test_scaffold_on_invoke
+ run_generator
+
+ # Model
+ assert_file "app/models/test_app/product_line.rb", /module TestApp\n class ProductLine < ActiveRecord::Base/
+ assert_file "test/models/test_app/product_line_test.rb", /module TestApp\n class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/product_lines.yml"
+ assert_migration "db/migrate/create_test_app_product_lines.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/resources :product_lines$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/product_lines_controller.rb",
+ /require_dependency "test_app\/application_controller"/,
+ /module TestApp/,
+ /class ProductLinesController < ApplicationController/
+
+ assert_file "test/controllers/test_app/product_lines_controller_test.rb",
+ /module TestApp\n class ProductLinesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(index edit new show _form).each do |view|
+ assert_file "app/views/test_app/product_lines/#{view}.html.erb"
+ end
+ assert_no_file "app/views/layouts/test_app/product_lines.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/test_app/product_lines_helper.rb"
+
+ # Stylesheets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_on_revoke
+ run_generator
+ run_generator ["product_line"], behavior: :revoke
+
+ # Model
+ assert_no_file "app/models/test_app/product_line.rb"
+ assert_no_file "test/models/test_app/product_line_test.rb"
+ assert_no_file "test/fixtures/test_app/product_lines.yml"
+ assert_no_migration "db/migrate/create_test_app_product_lines.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/resources :product_lines$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/test_app/product_lines_controller.rb"
+ assert_no_file "test/controllers/test_app/product_lines_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/test_app/product_lines"
+ assert_no_file "app/views/test_app/layouts/product_lines.html.erb"
+
+ # Helpers
+ assert_no_file "app/helpers/test_app/product_lines_helper.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_namespace_on_invoke
+ run_generator [ "admin/role", "name:string", "description:string" ]
+
+ # Model
+ assert_file "app/models/test_app/admin.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/role.rb", /module TestApp\n class Admin::Role < ActiveRecord::Base/
+ assert_file "test/models/test_app/admin/role_test.rb", /module TestApp\n class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::RolesController < ApplicationController/, content)
+ assert_match(%r(require_dependency "test_app/application_controller"), content)
+ end
+
+ assert_file "test/controllers/test_app/admin/roles_controller_test.rb",
+ /module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(index edit new show _form).each do |view|
+ assert_file "app/views/test_app/admin/roles/#{view}.html.erb"
+ end
+ assert_no_file "app/views/layouts/admin/roles.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/test_app/admin/roles_helper.rb"
+
+ # Stylesheets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_namespace_on_revoke
+ run_generator [ "admin/role", "name:string", "description:string" ]
+ run_generator [ "admin/role" ], behavior: :revoke
+
+ # Model
+ assert_file "app/models/test_app/admin.rb" # ( should not be remove )
+ assert_no_file "app/models/test_app/admin/role.rb"
+ assert_no_file "test/models/test_app/admin/role_test.rb"
+ assert_no_file "test/fixtures/test_app/admin/roles.yml"
+ assert_no_migration "db/migrate/create_test_app_admin_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/^ namespace :admin do\n resources :roles\n end$$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/test_app/admin/roles_controller.rb"
+ assert_no_file "test/controllers/test_app/admin/roles_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/test_app/admin/roles"
+ assert_no_file "app/views/layouts/test_app/admin/roles.html.erb"
+
+ # Helpers
+ assert_no_file "app/helpers/test_app/admin/roles_helper.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_invoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/user/special/role.rb", /module TestApp\n class Admin::User::Special::Role < ActiveRecord::Base/
+ assert_file "test/models/test_app/admin/user/special/role_test.rb", /module TestApp\n class Admin::User::Special::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/user/special/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::User::Special::RolesController < ApplicationController/, content)
+ end
+
+ assert_file "test/controllers/test_app/admin/user/special/roles_controller_test.rb",
+ /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(index edit new show _form).each do |view|
+ assert_file "app/views/test_app/admin/user/special/roles/#{view}.html.erb"
+ end
+ assert_no_file "app/views/layouts/admin/user/special/roles.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+
+ # Stylesheets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_revoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+ run_generator [ "admin/user/special/role" ], behavior: :revoke
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb" # ( should not be remove )
+ assert_no_file "app/models/test_app/admin/user/special/role.rb"
+ assert_no_file "test/models/test_app/admin/user/special/role_test.rb"
+ assert_no_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_no_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/test_app/admin/user/special/roles_controller.rb"
+ assert_no_file "test/controllers/test_app/admin/user/special/roles_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/test_app/admin/user/special/roles"
+
+ # Helpers
+ assert_no_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_api_scaffold_with_namespace_on_invoke
+ run_generator [ "admin/role", "name:string", "description:string", "--api" ]
+
+ # Model
+ assert_file "app/models/test_app/admin.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/role.rb", /module TestApp\n class Admin::Role < ActiveRecord::Base/
+ assert_file "test/models/test_app/admin/role_test.rb", /module TestApp\n class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::RolesController < ApplicationController/, content)
+ assert_match(%r(require_dependency "test_app/application_controller"), content)
+ end
+ assert_file "test/controllers/test_app/admin/roles_controller_test.rb",
+ /module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/
+ end
+end
diff --git a/railties/test/generators/orm_test.rb b/railties/test/generators/orm_test.rb
new file mode 100644
index 0000000000..88ae930554
--- /dev/null
+++ b/railties/test/generators/orm_test.rb
@@ -0,0 +1,38 @@
+require "generators/generators_test_helper"
+require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"
+
+# Mock out two ORMs
+module ORMWithGenerators
+ module Generators
+ class ActiveModel
+ def initialize(name)
+ end
+ end
+ end
+end
+
+module ORMWithoutGenerators
+ # No generators
+end
+
+class OrmTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ tests Rails::Generators::ScaffoldControllerGenerator
+
+ def test_orm_class_returns_custom_generator_if_supported_custom_orm_set
+ g = generator ["Foo"], orm: "ORMWithGenerators"
+ assert_equal ORMWithGenerators::Generators::ActiveModel, g.send(:orm_class)
+ end
+
+ def test_orm_class_returns_rails_generator_if_unsupported_custom_orm_set
+ g = generator ["Foo"], orm: "ORMWithoutGenerators"
+ assert_equal Rails::Generators::ActiveModel, g.send(:orm_class)
+ end
+
+ def test_orm_instance_returns_orm_class_instance_with_name
+ g = generator ["Foo"]
+ orm_instance = g.send(:orm_instance)
+ assert g.send(:orm_class) === orm_instance
+ assert_equal "foo", orm_instance.name
+ end
+end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
new file mode 100644
index 0000000000..60390cfa01
--- /dev/null
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -0,0 +1,636 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/plugin/plugin_generator'
+require 'generators/shared_generator_tests'
+
+DEFAULT_PLUGIN_FILES = %w(
+ .gitignore
+ Gemfile
+ Rakefile
+ README.rdoc
+ bukkits.gemspec
+ MIT-LICENSE
+ lib
+ lib/bukkits.rb
+ lib/tasks/bukkits_tasks.rake
+ lib/bukkits/version.rb
+ test/bukkits_test.rb
+ test/test_helper.rb
+ test/dummy
+)
+
+class PluginGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ destination File.join(Rails.root, "tmp/bukkits")
+ arguments [destination_root]
+
+ # brings setup, teardown, and some tests
+ include SharedGeneratorTests
+
+ def test_invalid_plugin_name_raises_an_error
+ content = capture(:stderr){ run_generator [File.join(destination_root, "my_plugin-31fr-extension")] }
+ assert_equal "Invalid plugin name my_plugin-31fr-extension. Please give a name which does not contain a namespace starting with numeric characters.\n", content
+
+ content = capture(:stderr){ run_generator [File.join(destination_root, "things4.3")] }
+ assert_equal "Invalid plugin name things4.3. Please give a name which uses only alphabetic, numeric, \"_\" or \"-\" characters.\n", content
+
+ content = capture(:stderr){ run_generator [File.join(destination_root, "43things")] }
+ assert_equal "Invalid plugin name 43things. Please give a name which does not start with numbers.\n", content
+
+ content = capture(:stderr){ run_generator [File.join(destination_root, "plugin")] }
+ assert_equal "Invalid plugin name plugin. Please give a name which does not match one of the reserved rails words: application, destroy, plugin, runner, test\n", content
+
+ content = capture(:stderr){ run_generator [File.join(destination_root, "Digest")] }
+ assert_equal "Invalid plugin name Digest, constant Digest is already in use. Please choose another plugin name.\n", content
+ end
+
+ def test_correct_file_in_lib_folder_of_hyphenated_plugin_name
+ run_generator [File.join(destination_root, "hyphenated-name")]
+ assert_no_file "hyphenated-name/lib/hyphenated-name.rb"
+ assert_no_file "hyphenated-name/lib/hyphenated_name.rb"
+ assert_file "hyphenated-name/lib/hyphenated/name.rb", /module Hyphenated\n module Name\n # Your code goes here...\n end\nend/
+ end
+
+ def test_correct_file_in_lib_folder_of_camelcase_plugin_name
+ run_generator [File.join(destination_root, "CamelCasedName")]
+ assert_no_file "CamelCasedName/lib/CamelCasedName.rb"
+ assert_file "CamelCasedName/lib/camel_cased_name.rb", /module CamelCasedName/
+ end
+
+ def test_generating_without_options
+ run_generator
+ assert_file "README.rdoc", /Bukkits/
+ assert_no_file "config/routes.rb"
+ assert_no_file "app/assets/config/bukkits_manifest.js"
+ assert_file "test/test_helper.rb" do |content|
+ assert_match(/require.+test\/dummy\/config\/environment/, content)
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+test\/dummy\/db\/migrate/, content)
+ assert_match(/Minitest\.backtrace_filter = Minitest::BacktraceFilter\.new/, content)
+ assert_match(/Rails::TestUnitReporter\.executable = 'bin\/test'/, content)
+ end
+ assert_file "test/bukkits_test.rb", /assert_kind_of Module, Bukkits/
+ assert_file 'bin/test'
+ assert_no_file 'bin/rails'
+ end
+
+ def test_generating_test_files_in_full_mode
+ run_generator [destination_root, "--full"]
+ assert_directory "test/integration/"
+
+ assert_file "test/integration/navigation_test.rb", /ActionDispatch::IntegrationTest/
+ end
+
+ def test_inclusion_of_a_debugger
+ run_generator [destination_root, '--full']
+ if defined?(JRUBY_VERSION) || RUBY_ENGINE == "rbx"
+ assert_file "Gemfile" do |content|
+ assert_no_match(/byebug/, content)
+ end
+ else
+ assert_file "Gemfile", /# gem 'byebug'/
+ end
+ end
+
+ def test_generating_test_files_in_full_mode_without_unit_test_files
+ run_generator [destination_root, "-T", "--full"]
+
+ assert_no_directory "test/integration/"
+ assert_no_file "test"
+ assert_file "Rakefile" do |contents|
+ assert_no_match(/APP_RAKEFILE/, contents)
+ end
+ end
+
+ def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files
+ run_generator [destination_root, "-T", "--mountable", '--dummy-path', 'my_dummy_app']
+ assert_file "Rakefile", /APP_RAKEFILE/
+ end
+
+ def test_generating_adds_dummy_app_without_javascript_and_assets_deps
+ run_generator
+
+ assert_file "test/dummy/app/assets/stylesheets/application.css"
+
+ assert_file "test/dummy/app/assets/javascripts/application.js" do |contents|
+ assert_no_match(/jquery/, contents)
+ end
+ end
+
+ def test_ensure_that_plugin_options_are_not_passed_to_app_generator
+ FileUtils.cd(Rails.root)
+ assert_no_match(/It works from file!.*It works_from_file/, run_generator([destination_root, "-m", "lib/template.rb"]))
+ end
+
+ def test_ensure_that_test_dummy_can_be_generated_from_a_template
+ FileUtils.cd(Rails.root)
+ run_generator([destination_root, "-m", "lib/create_test_dummy_template.rb", "--skip-test"])
+ assert_file "spec/dummy"
+ assert_no_file "test"
+ end
+
+ def test_database_entry_is_generated_for_sqlite3_by_default_in_full_mode
+ run_generator([destination_root, "--full"])
+ assert_file "test/dummy/config/database.yml", /sqlite/
+ assert_file "bukkits.gemspec", /sqlite3/
+ end
+
+ def test_config_another_database
+ run_generator([destination_root, "-d", "mysql", "--full"])
+ assert_file "test/dummy/config/database.yml", /mysql/
+ assert_file "bukkits.gemspec", /mysql/
+ end
+
+ def test_dont_generate_development_dependency
+ run_generator [destination_root, "--skip-active-record"]
+
+ assert_file "bukkits.gemspec" do |contents|
+ assert_no_match(/s.add_development_dependency "sqlite3"/, contents)
+ end
+ end
+
+ def test_app_generator_without_skips
+ run_generator
+ assert_file "test/dummy/config/application.rb", /\s+require\s+["']rails\/all["']/
+ assert_file "test/dummy/config/environments/development.rb" do |content|
+ assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content)
+ end
+ assert_file "test/dummy/config/environments/test.rb" do |content|
+ assert_match(/config\.action_mailer\.delivery_method = :test/, content)
+ end
+ assert_file "test/dummy/config/environments/production.rb" do |content|
+ assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content)
+ end
+ end
+
+ def test_active_record_is_removed_from_frameworks_if_skip_active_record_is_given
+ run_generator [destination_root, "--skip-active-record"]
+ assert_file "test/dummy/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
+ end
+
+ def test_ensure_that_skip_active_record_option_is_passed_to_app_generator
+ run_generator [destination_root, "--skip_active_record"]
+ assert_no_file "test/dummy/config/database.yml"
+ assert_file "test/test_helper.rb" do |contents|
+ assert_no_match(/ActiveRecord/, contents)
+ end
+ end
+
+ def test_action_mailer_is_removed_from_frameworks_if_skip_action_mailer_is_given
+ run_generator [destination_root, "--skip-action-mailer"]
+ assert_file "test/dummy/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/
+ assert_file "test/dummy/config/environments/development.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ assert_file "test/dummy/config/environments/test.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ assert_file "test/dummy/config/environments/production.rb" do |content|
+ assert_no_match(/config\.action_mailer/, content)
+ end
+ end
+
+ def test_ensure_that_database_option_is_passed_to_app_generator
+ run_generator [destination_root, "--database", "postgresql"]
+ assert_file "test/dummy/config/database.yml", /postgres/
+ end
+
+ def test_generation_runs_bundle_install_with_full_and_mountable
+ result = run_generator [destination_root, "--mountable", "--full", "--dev"]
+ assert_match(/run bundle install/, result)
+ assert $?.success?, "Command failed: #{result}"
+ assert_file "#{destination_root}/Gemfile.lock" do |contents|
+ assert_match(/bukkits/, contents)
+ end
+ end
+
+ def test_skipping_javascripts_without_mountable_option
+ run_generator
+ assert_no_file "app/assets/javascripts/bukkits/application.js"
+ end
+
+ def test_javascripts_generation
+ run_generator [destination_root, "--mountable"]
+ assert_file "app/assets/javascripts/bukkits/application.js"
+ end
+
+ def test_skip_javascripts
+ run_generator [destination_root, "--skip-javascript", "--mountable"]
+ assert_no_file "app/assets/javascripts/bukkits/application.js"
+ end
+
+ def test_template_from_dir_pwd
+ FileUtils.cd(Rails.root)
+ assert_match(/It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"]))
+ end
+
+ def test_ensure_that_tests_work
+ run_generator
+ FileUtils.cd destination_root
+ quietly { system 'bundle install' }
+ assert_match(/1 runs, 1 assertions, 0 failures, 0 errors/, `bin/test 2>&1`)
+ end
+
+ def test_ensure_that_tests_works_in_full_mode
+ run_generator [destination_root, "--full", "--skip_active_record"]
+ FileUtils.cd destination_root
+ quietly { system 'bundle install' }
+ assert_match(/1 runs, 1 assertions, 0 failures, 0 errors/, `bundle exec rake test 2>&1`)
+ end
+
+ def test_ensure_that_migration_tasks_work_with_mountable_option
+ run_generator [destination_root, "--mountable"]
+ FileUtils.cd destination_root
+ quietly { system 'bundle install' }
+ output = `bundle exec rake db:migrate 2>&1`
+ assert $?.success?, "Command failed: #{output}"
+ end
+
+ def test_creating_engine_in_full_mode
+ run_generator [destination_root, "--full"]
+ assert_file "app/assets/javascripts/bukkits"
+ assert_file "app/assets/stylesheets/bukkits"
+ assert_file "app/assets/images/bukkits"
+ assert_file "app/models"
+ assert_file "app/controllers"
+ assert_file "app/views"
+ assert_file "app/helpers"
+ assert_file "app/mailers"
+ assert_file "bin/rails"
+ assert_file "config/routes.rb", /Rails.application.routes.draw do/
+ assert_file "lib/bukkits/engine.rb", /module Bukkits\n class Engine < ::Rails::Engine\n end\nend/
+ assert_file "lib/bukkits.rb", /require "bukkits\/engine"/
+ end
+
+ def test_creating_engine_with_hyphenated_name_in_full_mode
+ run_generator [File.join(destination_root, "hyphenated-name"), "--full"]
+ assert_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
+ assert_file "hyphenated-name/app/assets/stylesheets/hyphenated/name"
+ assert_file "hyphenated-name/app/assets/images/hyphenated/name"
+ assert_file "hyphenated-name/app/models"
+ assert_file "hyphenated-name/app/controllers"
+ assert_file "hyphenated-name/app/views"
+ assert_file "hyphenated-name/app/helpers"
+ assert_file "hyphenated-name/app/mailers"
+ assert_file "hyphenated-name/bin/rails"
+ assert_file "hyphenated-name/config/routes.rb", /Rails.application.routes.draw do/
+ assert_file "hyphenated-name/lib/hyphenated/name/engine.rb", /module Hyphenated\n module Name\n class Engine < ::Rails::Engine\n end\n end\nend/
+ assert_file "hyphenated-name/lib/hyphenated/name.rb", /require "hyphenated\/name\/engine"/
+ assert_file "hyphenated-name/bin/rails", /\.\.\/\.\.\/lib\/hyphenated\/name\/engine/
+ end
+
+ def test_creating_engine_with_hyphenated_and_underscored_name_in_full_mode
+ run_generator [File.join(destination_root, "my_hyphenated-name"), "--full"]
+ assert_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
+ assert_file "my_hyphenated-name/app/assets/stylesheets/my_hyphenated/name"
+ assert_file "my_hyphenated-name/app/assets/images/my_hyphenated/name"
+ assert_file "my_hyphenated-name/app/models"
+ assert_file "my_hyphenated-name/app/controllers"
+ assert_file "my_hyphenated-name/app/views"
+ assert_file "my_hyphenated-name/app/helpers"
+ assert_file "my_hyphenated-name/app/mailers"
+ assert_file "my_hyphenated-name/bin/rails"
+ assert_file "my_hyphenated-name/config/routes.rb", /Rails.application.routes.draw do/
+ assert_file "my_hyphenated-name/lib/my_hyphenated/name/engine.rb", /module MyHyphenated\n module Name\n class Engine < ::Rails::Engine\n end\n end\nend/
+ assert_file "my_hyphenated-name/lib/my_hyphenated/name.rb", /require "my_hyphenated\/name\/engine"/
+ assert_file "my_hyphenated-name/bin/rails", /\.\.\/\.\.\/lib\/my_hyphenated\/name\/engine/
+ end
+
+ def test_being_quiet_while_creating_dummy_application
+ assert_no_match(/create\s+config\/application.rb/, run_generator)
+ end
+
+ def test_create_mountable_application_with_mountable_option
+ run_generator [destination_root, "--mountable"]
+ assert_file "app/assets/javascripts/bukkits"
+ assert_file "app/assets/stylesheets/bukkits"
+ assert_file "app/assets/images/bukkits"
+ assert_file "config/routes.rb", /Bukkits::Engine.routes.draw do/
+ assert_file "lib/bukkits/engine.rb", /isolate_namespace Bukkits/
+ assert_file "test/dummy/config/routes.rb", /mount Bukkits::Engine => "\/bukkits"/
+ assert_file "app/controllers/bukkits/application_controller.rb", /module Bukkits\n class ApplicationController < ActionController::Base/
+ assert_file "app/jobs/bukkits/application_job.rb", /module Bukkits\n class ApplicationJob < ActiveJob::Base/
+ assert_file "app/helpers/bukkits/application_helper.rb", /module Bukkits\n module ApplicationHelper/
+ assert_file "app/views/layouts/bukkits/application.html.erb" do |contents|
+ assert_match "<title>Bukkits</title>", contents
+ assert_match(/stylesheet_link_tag\s+['"]bukkits\/application['"]/, contents)
+ assert_match(/javascript_include_tag\s+['"]bukkits\/application['"]/, contents)
+ end
+ assert_file "test/test_helper.rb" do |content|
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+\.\.\/test\/dummy\/db\/migrate/, content)
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+<<.+\.\.\/db\/migrate/, content)
+ assert_match(/ActionDispatch::IntegrationTest\.fixture_path = ActiveSupport::TestCase\.fixture_pat/, content)
+ assert_no_match(/Rails::TestUnitReporter\.executable = 'bin\/test'/, content)
+ end
+ assert_no_file 'bin/test'
+ end
+
+ def test_create_mountable_application_with_mountable_option_and_hypenated_name
+ run_generator [File.join(destination_root, "hyphenated-name"), "--mountable"]
+ assert_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
+ assert_file "hyphenated-name/app/assets/stylesheets/hyphenated/name"
+ assert_file "hyphenated-name/app/assets/images/hyphenated/name"
+ assert_file "hyphenated-name/config/routes.rb", /Hyphenated::Name::Engine.routes.draw do/
+ assert_file "hyphenated-name/lib/hyphenated/name/version.rb", /module Hyphenated\n module Name\n VERSION = '0.1.0'\n end\nend/
+ assert_file "hyphenated-name/lib/hyphenated/name/engine.rb", /module Hyphenated\n module Name\n class Engine < ::Rails::Engine\n isolate_namespace Hyphenated::Name\n end\n end\nend/
+ assert_file "hyphenated-name/lib/hyphenated/name.rb", /require "hyphenated\/name\/engine"/
+ assert_file "hyphenated-name/test/dummy/config/routes.rb", /mount Hyphenated::Name::Engine => "\/hyphenated-name"/
+ assert_file "hyphenated-name/app/controllers/hyphenated/name/application_controller.rb", /module Hyphenated\n module Name\n class ApplicationController < ActionController::Base\n end\n end\nend/
+ assert_file "hyphenated-name/app/jobs/hyphenated/name/application_job.rb", /module Hyphenated\n module Name\n class ApplicationJob < ActiveJob::Base/
+ assert_file "hyphenated-name/app/helpers/hyphenated/name/application_helper.rb", /module Hyphenated\n module Name\n module ApplicationHelper\n end\n end\nend/
+ assert_file "hyphenated-name/app/views/layouts/hyphenated/name/application.html.erb" do |contents|
+ assert_match "<title>Hyphenated name</title>", contents
+ assert_match(/stylesheet_link_tag\s+['"]hyphenated\/name\/application['"]/, contents)
+ assert_match(/javascript_include_tag\s+['"]hyphenated\/name\/application['"]/, contents)
+ end
+ end
+
+ def test_create_mountable_application_with_mountable_option_and_hypenated_and_underscored_name
+ run_generator [File.join(destination_root, "my_hyphenated-name"), "--mountable"]
+ assert_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
+ assert_file "my_hyphenated-name/app/assets/stylesheets/my_hyphenated/name"
+ assert_file "my_hyphenated-name/app/assets/images/my_hyphenated/name"
+ assert_file "my_hyphenated-name/config/routes.rb", /MyHyphenated::Name::Engine.routes.draw do/
+ assert_file "my_hyphenated-name/lib/my_hyphenated/name/version.rb", /module MyHyphenated\n module Name\n VERSION = '0.1.0'\n end\nend/
+ assert_file "my_hyphenated-name/lib/my_hyphenated/name/engine.rb", /module MyHyphenated\n module Name\n class Engine < ::Rails::Engine\n isolate_namespace MyHyphenated::Name\n end\n end\nend/
+ assert_file "my_hyphenated-name/lib/my_hyphenated/name.rb", /require "my_hyphenated\/name\/engine"/
+ assert_file "my_hyphenated-name/test/dummy/config/routes.rb", /mount MyHyphenated::Name::Engine => "\/my_hyphenated-name"/
+ assert_file "my_hyphenated-name/app/controllers/my_hyphenated/name/application_controller.rb", /module MyHyphenated\n module Name\n class ApplicationController < ActionController::Base\n end\n end\nend/
+ assert_file "my_hyphenated-name/app/jobs/my_hyphenated/name/application_job.rb", /module MyHyphenated\n module Name\n class ApplicationJob < ActiveJob::Base/
+ assert_file "my_hyphenated-name/app/helpers/my_hyphenated/name/application_helper.rb", /module MyHyphenated\n module Name\n module ApplicationHelper\n end\n end\nend/
+ assert_file "my_hyphenated-name/app/views/layouts/my_hyphenated/name/application.html.erb" do |contents|
+ assert_match "<title>My hyphenated name</title>", contents
+ assert_match(/stylesheet_link_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
+ assert_match(/javascript_include_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
+ end
+ end
+
+ def test_create_mountable_application_with_mountable_option_and_multiple_hypenates_in_name
+ run_generator [File.join(destination_root, "deep-hyphenated-name"), "--mountable"]
+ assert_file "deep-hyphenated-name/app/assets/javascripts/deep/hyphenated/name"
+ assert_file "deep-hyphenated-name/app/assets/stylesheets/deep/hyphenated/name"
+ assert_file "deep-hyphenated-name/app/assets/images/deep/hyphenated/name"
+ assert_file "deep-hyphenated-name/config/routes.rb", /Deep::Hyphenated::Name::Engine.routes.draw do/
+ assert_file "deep-hyphenated-name/lib/deep/hyphenated/name/version.rb", /module Deep\n module Hyphenated\n module Name\n VERSION = '0.1.0'\n end\n end\nend/
+ assert_file "deep-hyphenated-name/lib/deep/hyphenated/name/engine.rb", /module Deep\n module Hyphenated\n module Name\n class Engine < ::Rails::Engine\n isolate_namespace Deep::Hyphenated::Name\n end\n end\n end\nend/
+ assert_file "deep-hyphenated-name/lib/deep/hyphenated/name.rb", /require "deep\/hyphenated\/name\/engine"/
+ assert_file "deep-hyphenated-name/test/dummy/config/routes.rb", /mount Deep::Hyphenated::Name::Engine => "\/deep-hyphenated-name"/
+ assert_file "deep-hyphenated-name/app/controllers/deep/hyphenated/name/application_controller.rb", /module Deep\n module Hyphenated\n module Name\n class ApplicationController < ActionController::Base\n end\n end\n end\nend/
+ assert_file "deep-hyphenated-name/app/jobs/deep/hyphenated/name/application_job.rb", /module Deep\n module Hyphenated\n module Name\n class ApplicationJob < ActiveJob::Base/
+ assert_file "deep-hyphenated-name/app/helpers/deep/hyphenated/name/application_helper.rb", /module Deep\n module Hyphenated\n module Name\n module ApplicationHelper\n end\n end\n end\nend/
+ assert_file "deep-hyphenated-name/app/views/layouts/deep/hyphenated/name/application.html.erb" do |contents|
+ assert_match "<title>Deep hyphenated name</title>", contents
+ assert_match(/stylesheet_link_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
+ assert_match(/javascript_include_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
+ end
+ end
+
+ def test_creating_gemspec
+ run_generator
+ assert_file "bukkits.gemspec", /s.name\s+= "bukkits"/
+ assert_file "bukkits.gemspec", /s.files = Dir\["\{app,config,db,lib\}\/\*\*\/\*", "MIT-LICENSE", "Rakefile", "README\.rdoc"\]/
+ assert_file "bukkits.gemspec", /s.version\s+ = Bukkits::VERSION/
+ end
+
+ def test_usage_of_engine_commands
+ run_generator [destination_root, "--full"]
+ assert_file "bin/rails", /ENGINE_PATH = File.expand_path\('..\/..\/lib\/bukkits\/engine', __FILE__\)/
+ assert_file "bin/rails", /ENGINE_ROOT = File.expand_path\('..\/..', __FILE__\)/
+ assert_file "bin/rails", /require 'rails\/all'/
+ assert_file "bin/rails", /require 'rails\/engine\/commands'/
+ end
+
+ def test_shebang
+ run_generator [destination_root, "--full"]
+ assert_file "bin/rails", /#!\/usr\/bin\/env ruby/
+ end
+
+ def test_passing_dummy_path_as_a_parameter
+ run_generator [destination_root, "--dummy_path", "spec/dummy"]
+ assert_file "spec/dummy"
+ assert_file "spec/dummy/config/application.rb"
+ assert_no_file "test/dummy"
+ assert_file "test/test_helper.rb" do |content|
+ assert_match(/require.+spec\/dummy\/config\/environment/, content)
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+spec\/dummy\/db\/migrate/, content)
+ end
+ end
+
+ def test_creating_dummy_application_with_different_name
+ run_generator [destination_root, "--dummy_path", "spec/fake"]
+ assert_file "spec/fake"
+ assert_file "spec/fake/config/application.rb"
+ assert_no_file "test/dummy"
+ assert_file "test/test_helper.rb" do |content|
+ assert_match(/require.+spec\/fake\/config\/environment/, content)
+ assert_match(/ActiveRecord::Migrator\.migrations_paths.+spec\/fake\/db\/migrate/, content)
+ end
+ end
+
+ def test_creating_dummy_without_tests_but_with_dummy_path
+ run_generator [destination_root, "--dummy_path", "spec/dummy", "--skip-test"]
+ assert_file "spec/dummy"
+ assert_file "spec/dummy/config/application.rb"
+ assert_no_file "test"
+ assert_no_file "test/test_helper.rb"
+ assert_file '.gitignore' do |contents|
+ assert_match(/spec\/dummy/, contents)
+ end
+ end
+
+ def test_ensure_that_gitignore_can_be_generated_from_a_template_for_dummy_path
+ FileUtils.cd(Rails.root)
+ run_generator([destination_root, "--dummy_path", "spec/dummy", "--skip-test"])
+ assert_file ".gitignore" do |contents|
+ assert_match(/spec\/dummy/, contents)
+ end
+ end
+
+ def test_unnecessary_files_are_not_generated_in_dummy_application
+ run_generator
+ assert_no_file 'test/dummy/.gitignore'
+ assert_no_file 'test/dummy/db/seeds.rb'
+ assert_no_file 'test/dummy/Gemfile'
+ assert_no_file 'test/dummy/public/robots.txt'
+ assert_no_file 'test/dummy/README.md'
+ assert_no_directory 'test/dummy/lib/tasks'
+ assert_no_directory 'test/dummy/doc'
+ assert_no_directory 'test/dummy/test'
+ assert_no_directory 'test/dummy/vendor'
+ end
+
+ def test_skipping_test_files
+ run_generator [destination_root, "--skip-test"]
+ assert_no_file "test"
+ assert_file '.gitignore' do |contents|
+ assert_no_match(/test\dummy/, contents)
+ end
+ end
+
+ def test_skipping_gemspec
+ run_generator [destination_root, "--skip-gemspec"]
+ assert_no_file "bukkits.gemspec"
+ assert_file "Gemfile" do |contents|
+ assert_no_match('gemspec', contents)
+ assert_match(/gem 'rails', '~> #{Rails.version}'/, contents)
+ assert_match_sqlite3(contents)
+ assert_no_match(/# gem "jquery-rails"/, contents)
+ end
+ end
+
+ def test_skipping_gemspec_in_full_mode
+ run_generator [destination_root, "--skip-gemspec", "--full"]
+ assert_no_file "bukkits.gemspec"
+ assert_file "Gemfile" do |contents|
+ assert_no_match('gemspec', contents)
+ assert_match(/gem 'rails', '~> #{Rails.version}'/, contents)
+ assert_match_sqlite3(contents)
+ end
+ end
+
+ def test_creating_plugin_in_app_directory_adds_gemfile_entry
+ # simulate application existence
+ gemfile_path = "#{Rails.root}/Gemfile"
+ Object.const_set('APP_PATH', Rails.root)
+ FileUtils.touch gemfile_path
+
+ run_generator
+
+ assert_file gemfile_path, /gem 'bukkits', path: 'tmp\/bukkits'/
+ ensure
+ Object.send(:remove_const, 'APP_PATH')
+ FileUtils.rm gemfile_path
+ end
+
+ def test_skipping_gemfile_entry
+ # simulate application existence
+ gemfile_path = "#{Rails.root}/Gemfile"
+ Object.const_set('APP_PATH', Rails.root)
+ FileUtils.touch gemfile_path
+
+ run_generator [destination_root, "--skip-gemfile-entry"]
+
+ assert_file gemfile_path do |contents|
+ assert_no_match(/gem 'bukkits', path: 'tmp\/bukkits'/, contents)
+ end
+ ensure
+ Object.send(:remove_const, 'APP_PATH')
+ FileUtils.rm gemfile_path
+ end
+
+ def test_generating_controller_inside_mountable_engine
+ run_generator [destination_root, "--mountable"]
+
+ capture(:stdout) do
+ `#{destination_root}/bin/rails g controller admin/dashboard foo`
+ end
+
+ assert_file "config/routes.rb" do |contents|
+ assert_match(/namespace :admin/, contents)
+ assert_no_match(/namespace :bukkit/, contents)
+ end
+ end
+
+ def test_git_name_and_email_in_gemspec_file
+ name = `git config user.name`.chomp rescue "TODO: Write your name"
+ email = `git config user.email`.chomp rescue "TODO: Write your email address"
+
+ run_generator
+ assert_file "bukkits.gemspec" do |contents|
+ assert_match name, contents
+ assert_match email, contents
+ end
+ end
+
+ def test_git_name_in_license_file
+ name = `git config user.name`.chomp rescue "TODO: Write your name"
+
+ run_generator
+ assert_file "MIT-LICENSE" do |contents|
+ assert_match name, contents
+ end
+ end
+
+ def test_no_details_from_git_when_skip_git
+ name = "TODO: Write your name"
+ email = "TODO: Write your email address"
+
+ run_generator [destination_root, '--skip-git']
+ assert_file "MIT-LICENSE" do |contents|
+ assert_match name, contents
+ end
+ assert_file "bukkits.gemspec" do |contents|
+ assert_match name, contents
+ assert_match email, contents
+ end
+ end
+
+ def test_skipping_useless_folders_generation_for_api_engines
+ ['--full', '--mountable'].each do |option|
+ run_generator [destination_root, option, '--api']
+
+ assert_no_directory "app/assets"
+ assert_no_directory "app/helpers"
+ assert_no_directory "app/views"
+
+ FileUtils.rm_rf destination_root
+ end
+ end
+
+ def test_application_controller_parent_for_mountable_api_plugins
+ run_generator [destination_root, '--mountable', '--api']
+
+ assert_file "app/controllers/bukkits/application_controller.rb" do |content|
+ assert_match "ApplicationController < ActionController::API", content
+ end
+ end
+
+ def test_dummy_api_application_for_api_plugins
+ run_generator [destination_root, '--api']
+
+ assert_file "test/dummy/config/application.rb" do |content|
+ assert_match "config.api_only = true", content
+ end
+ end
+
+
+ def test_api_generators_configuration_for_api_engines
+ run_generator [destination_root, '--full', '--api']
+
+ assert_file "lib/bukkits/engine.rb" do |content|
+ assert_match "config.generators.api_only = true", content
+ end
+ end
+
+ def test_scaffold_generator_for_mountable_api_plugins
+ run_generator [destination_root, '--mountable', '--api']
+
+ capture(:stdout) do
+ `#{destination_root}/bin/rails g scaffold article`
+ end
+
+ assert_file "app/models/bukkits/article.rb"
+ assert_file "app/controllers/bukkits/articles_controller.rb" do |content|
+ assert_match "only: [:show, :update, :destroy]", content
+ end
+
+ assert_no_directory "app/assets"
+ assert_no_directory "app/helpers"
+ assert_no_directory "app/views"
+ end
+
+protected
+ def action(*args, &block)
+ silence(:stdout){ generator.send(*args, &block) }
+ end
+
+ def default_files
+ ::DEFAULT_PLUGIN_FILES
+ end
+
+ def assert_match_sqlite3(contents)
+ if defined?(JRUBY_VERSION)
+ assert_match(/group :development do\n gem 'activerecord-jdbcsqlite3-adapter'\nend/, contents)
+ else
+ assert_match(/group :development do\n gem 'sqlite3'\nend/, contents)
+ end
+ end
+end
diff --git a/railties/test/generators/plugin_test_helper.rb b/railties/test/generators/plugin_test_helper.rb
new file mode 100644
index 0000000000..96c1b1d31f
--- /dev/null
+++ b/railties/test/generators/plugin_test_helper.rb
@@ -0,0 +1,24 @@
+require 'abstract_unit'
+require 'tmpdir'
+
+module PluginTestHelper
+ def create_test_file(name, pass: true)
+ plugin_file "test/#{name}_test.rb", <<-RUBY
+ require 'test_helper'
+
+ class #{name.camelize}Test < ActiveSupport::TestCase
+ def test_truth
+ puts "#{name.camelize}Test"
+ assert #{pass}, 'wups!'
+ end
+ end
+ RUBY
+ end
+
+ def plugin_file(path, contents, mode: 'w')
+ FileUtils.mkdir_p File.dirname("#{plugin_path}/#{path}")
+ File.open("#{plugin_path}/#{path}", mode) do |f|
+ f.puts contents
+ end
+ end
+end
diff --git a/railties/test/generators/plugin_test_runner_test.rb b/railties/test/generators/plugin_test_runner_test.rb
new file mode 100644
index 0000000000..716728819e
--- /dev/null
+++ b/railties/test/generators/plugin_test_runner_test.rb
@@ -0,0 +1,104 @@
+require 'generators/plugin_test_helper'
+
+class PluginTestRunnerTest < ActiveSupport::TestCase
+ include PluginTestHelper
+
+ def setup
+ @destination_root = Dir.mktmpdir('bukkits')
+ Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --skip-bundle` }
+ plugin_file 'test/dummy/db/schema.rb', ''
+ end
+
+ def teardown
+ FileUtils.rm_rf(@destination_root)
+ end
+
+ def test_run_single_file
+ create_test_file 'foo'
+ create_test_file 'bar'
+ assert_match "1 runs, 1 assertions, 0 failures", run_test_command("test/foo_test.rb")
+ end
+
+ def test_run_multiple_files
+ create_test_file 'foo'
+ create_test_file 'bar'
+ assert_match "2 runs, 2 assertions, 0 failures", run_test_command("test/foo_test.rb test/bar_test.rb")
+ end
+
+ def test_mix_files_and_line_filters
+ create_test_file 'account'
+ plugin_file 'test/post_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ def test_post
+ puts 'PostTest'
+ assert true
+ end
+
+ def test_line_filter_does_not_run_this
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command('test/account_test.rb test/post_test.rb:4').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'PostTest', output
+ assert_match '2 runs, 2 assertions', output
+ end
+ end
+
+ def test_multiple_line_filters
+ create_test_file 'account'
+ create_test_file 'post'
+
+ run_test_command('test/account_test.rb:4 test/post_test.rb:4').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'PostTest', output
+ end
+ end
+
+ def test_line_filter_without_line_runs_all_tests
+ create_test_file 'account'
+
+ run_test_command('test/account_test.rb:').tap do |output|
+ assert_match 'AccountTest', output
+ end
+ end
+
+ def test_output_inline_by_default
+ create_test_file 'post', pass: false
+
+ output = run_test_command('test/post_test.rb')
+ assert_match %r{Running:\n\nPostTest\nF\n\nwups!\n\nbin/test (/private)?#{plugin_path}/test/post_test.rb:6}, output
+ end
+
+ def test_only_inline_failure_output
+ create_test_file 'post', pass: false
+
+ output = run_test_command('test/post_test.rb')
+ assert_match %r{Finished in.*\n\n1 runs, 1 assertions}, output
+ end
+
+ def test_fail_fast
+ create_test_file 'post', pass: false
+
+ assert_match(/Interrupt/,
+ capture(:stderr) { run_test_command('test/post_test.rb --fail-fast') })
+ end
+
+ def test_raise_error_when_specified_file_does_not_exist
+ error = capture(:stderr) { run_test_command('test/not_exists.rb') }
+ assert_match(%r{cannot load such file.+test/not_exists\.rb}, error)
+ end
+
+ private
+ def plugin_path
+ "#{@destination_root}/bukkits"
+ end
+
+ def run_test_command(arguments)
+ Dir.chdir(plugin_path) { `bin/test #{arguments}` }
+ end
+end
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
new file mode 100644
index 0000000000..581d80d60e
--- /dev/null
+++ b/railties/test/generators/resource_generator_test.rb
@@ -0,0 +1,88 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/resource/resource_generator'
+
+class ResourceGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(account)
+
+ setup :copy_routes
+
+ def test_help_with_inherited_options
+ content = run_generator ["--help"]
+ assert_match(/ActiveRecord options:/, content)
+ assert_match(/TestUnit options:/, content)
+ end
+
+ def test_files_from_inherited_invocation
+ run_generator
+
+ %w(
+ app/models/account.rb
+ test/models/account_test.rb
+ test/fixtures/accounts.yml
+ ).each { |path| assert_file path }
+
+ assert_migration "db/migrate/create_accounts.rb"
+ end
+
+ def test_inherited_invocations_with_attributes
+ run_generator ["account", "name:string"]
+ assert_migration "db/migrate/create_accounts.rb", /t.string :name/
+ end
+
+ def test_resource_controller_with_pluralized_class_name
+ run_generator
+ assert_file "app/controllers/accounts_controller.rb", /class AccountsController < ApplicationController/
+ assert_file "test/controllers/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/
+
+ assert_file "app/helpers/accounts_helper.rb", /module AccountsHelper/
+ end
+
+ def test_resource_controller_with_actions
+ run_generator ["account", "--actions", "index", "new"]
+
+ assert_file "app/controllers/accounts_controller.rb" do |controller|
+ assert_instance_method :index, controller
+ assert_instance_method :new, controller
+ end
+
+ assert_file "app/views/accounts/index.html.erb"
+ assert_file "app/views/accounts/new.html.erb"
+ end
+
+ def test_resource_routes_are_added
+ run_generator
+
+ assert_file "config/routes.rb" do |route|
+ assert_match(/resources :accounts$/, route)
+ end
+ end
+
+ def test_plural_names_are_singularized
+ content = run_generator ["accounts".freeze]
+ assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/
+ assert_file "test/models/account_test.rb", /class AccountTest/
+ assert_match(/\[WARNING\] The model name 'accounts' was recognized as a plural, using the singular 'account' instead\. Override with --force-plural or setup custom inflection rules for this noun before running the generator\./, content)
+ end
+
+ def test_plural_names_can_be_forced
+ content = run_generator ["accounts", "--force-plural"]
+ assert_file "app/models/accounts.rb", /class Accounts < ActiveRecord::Base/
+ assert_file "test/models/accounts_test.rb", /class AccountsTest/
+ assert_no_match(/\[WARNING\]/, content)
+ end
+
+ def test_mass_nouns_do_not_throw_warnings
+ content = run_generator ["sheep".freeze]
+ assert_no_match(/\[WARNING\]/, content)
+ end
+
+ def test_route_is_removed_on_revoke
+ run_generator
+ run_generator ["account"], behavior: :revoke
+
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/resources :accounts$/, route)
+ end
+ end
+end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
new file mode 100644
index 0000000000..b4ba101017
--- /dev/null
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -0,0 +1,246 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
+
+module Unknown
+ module Generators
+ end
+end
+
+class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(User name:string age:integer)
+
+ def test_controller_skeleton_is_created
+ run_generator
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match(/class UsersController < ApplicationController/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@users = User\.all/, m)
+ end
+
+ assert_instance_method :show, content
+
+ assert_instance_method :new, content do |m|
+ assert_match(/@user = User\.new/, m)
+ end
+
+ assert_instance_method :edit, content
+
+ assert_instance_method :create, content do |m|
+ assert_match(/@user = User\.new\(user_params\)/, m)
+ assert_match(/@user\.save/, m)
+ end
+
+ assert_instance_method :update, content do |m|
+ assert_match(/@user\.update\(user_params\)/, m)
+ end
+
+ assert_instance_method :destroy, content do |m|
+ assert_match(/@user\.destroy/, m)
+ assert_match(/User was successfully destroyed/, m)
+ end
+
+ assert_instance_method :set_user, content do |m|
+ assert_match(/@user = User\.find\(params\[:id\]\)/, m)
+ end
+
+ assert_match(/def user_params/, content)
+ assert_match(/params\.require\(:user\)\.permit\(:name, :age\)/, content)
+ end
+ end
+
+ def test_dont_use_require_or_permit_if_there_are_no_attributes
+ run_generator ["User"]
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match(/def user_params/, content)
+ assert_match(/params\.fetch\(:user, \{\}\)/, content)
+ end
+ end
+
+ def test_controller_permit_references_attributes
+ run_generator ["LineItem", "product:references", "cart:belongs_to"]
+
+ assert_file "app/controllers/line_items_controller.rb" do |content|
+ assert_match(/def line_item_params/, content)
+ assert_match(/params\.require\(:line_item\)\.permit\(:product_id, :cart_id\)/, content)
+ end
+ end
+
+ def test_controller_permit_polymorphic_references_attributes
+ run_generator ["LineItem", "product:references{polymorphic}"]
+
+ assert_file "app/controllers/line_items_controller.rb" do |content|
+ assert_match(/def line_item_params/, content)
+ assert_match(/params\.require\(:line_item\)\.permit\(:product_id, :product_type\)/, content)
+ end
+ end
+
+ def test_helper_are_invoked_with_a_pluralized_name
+ run_generator
+ assert_file "app/helpers/users_helper.rb", /module UsersHelper/
+ end
+
+ def test_views_are_generated
+ run_generator
+
+ %w(index edit new show).each do |view|
+ assert_file "app/views/users/#{view}.html.erb"
+ end
+ assert_no_file "app/views/layouts/users.html.erb"
+ end
+
+ def test_index_page_have_notice
+ run_generator
+
+ %w(index show).each do |view|
+ assert_file "app/views/users/#{view}.html.erb", /notice/
+ end
+ end
+
+ def test_functional_tests
+ run_generator ["User", "name:string", "age:integer", "organization:references{polymorphic}"]
+
+ assert_file "test/controllers/users_controller_test.rb" do |content|
+ assert_match(/class UsersControllerTest < ActionController::TestCase/, content)
+ assert_match(/test "should get index"/, content)
+ assert_match(/post :create, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ assert_match(/patch :update, params: \{ id: @user, user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ end
+ end
+
+ def test_functional_tests_without_attributes
+ run_generator ["User"]
+
+ assert_file "test/controllers/users_controller_test.rb" do |content|
+ assert_match(/class UsersControllerTest < ActionController::TestCase/, content)
+ assert_match(/test "should get index"/, content)
+ assert_match(/post :create, params: \{ user: \{ \} \}/, content)
+ assert_match(/patch :update, params: \{ id: @user, user: \{ \} \}/, content)
+ end
+ end
+
+ def test_skip_helper_if_required
+ run_generator ["User", "name:string", "age:integer", "--no-helper"]
+ assert_no_file "app/helpers/users_helper.rb"
+ end
+
+ def test_skip_layout_if_required
+ run_generator ["User", "name:string", "age:integer", "--no-layout"]
+ assert_no_file "app/views/layouts/users.html.erb"
+ end
+
+ def test_default_orm_is_used
+ run_generator ["User", "--orm=unknown"]
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match(/class UsersController < ApplicationController/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@users = User\.all/, m)
+ end
+ end
+ end
+
+ def test_customized_orm_is_used
+ klass = Class.new(Rails::Generators::ActiveModel) do
+ def self.all(klass)
+ "#{klass}.find(:all)"
+ end
+ end
+
+ Unknown::Generators.const_set(:ActiveModel, klass)
+ run_generator ["User", "--orm=unknown"]
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match(/class UsersController < ApplicationController/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@users = User\.find\(:all\)/, m)
+ assert_no_match(/@users = User\.all/, m)
+ end
+ end
+ ensure
+ Unknown::Generators.send :remove_const, :ActiveModel
+ end
+
+ def test_model_name_option
+ run_generator ["Admin::User", "--model-name=User"]
+ assert_file "app/controllers/admin/users_controller.rb" do |content|
+ assert_instance_method :index, content do |m|
+ assert_match("@users = User.all", m)
+ end
+ end
+ end
+
+ def test_controller_tests_pass_by_default_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 g controller dashboard foo` }
+ assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ end
+ end
+
+ def test_controller_tests_pass_by_default_inside_full_engine
+ Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --full` }
+
+ engine_path = File.join(destination_root, "bukkits")
+
+ Dir.chdir(engine_path) do
+ quietly { `bin/rails g controller dashboard foo` }
+ assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ end
+ end
+
+ def test_api_only_generates_a_proper_api_controller
+ run_generator ["User", "--api"]
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match(/class UsersController < ApplicationController/, content)
+ assert_no_match(/respond_to/, content)
+
+ assert_match(/before_action :set_user, only: \[:show, :update, :destroy\]/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@users = User\.all/, m)
+ assert_match(/render json: @users/, m)
+ end
+
+ assert_instance_method :show, content do |m|
+ assert_match(/render json: @user/, m)
+ end
+
+ assert_instance_method :create, content do |m|
+ assert_match(/@user = User\.new\(user_params\)/, m)
+ assert_match(/@user\.save/, m)
+ assert_match(/@user\.errors/, m)
+ end
+
+ assert_instance_method :update, content do |m|
+ assert_match(/@user\.update\(user_params\)/, m)
+ assert_match(/@user\.errors/, m)
+ end
+
+ assert_instance_method :destroy, content do |m|
+ assert_match(/@user\.destroy/, m)
+ end
+ end
+ end
+
+ def test_api_controller_tests
+ run_generator ["User", "name:string", "age:integer", "organization:references{polymorphic}", "--api"]
+
+ assert_file "test/controllers/users_controller_test.rb" do |content|
+ assert_match(/class UsersControllerTest < ActionController::TestCase/, content)
+ assert_match(/test "should get index"/, content)
+ assert_match(/post :create, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ assert_match(/patch :update, params: \{ id: @user, user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ assert_no_match(/assert_redirected_to/, content)
+ end
+ end
+end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
new file mode 100644
index 0000000000..0c3808a9a0
--- /dev/null
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -0,0 +1,536 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/scaffold/scaffold_generator'
+
+class ScaffoldGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(product_line title:string product:belongs_to user:references)
+
+ setup :copy_routes
+
+ def test_scaffold_on_invoke
+ run_generator
+
+ # Model
+ assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
+ assert_file "test/models/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/product_lines.yml"
+ assert_migration "db/migrate/create_product_lines.rb", /belongs_to :product, index: true/
+ assert_migration "db/migrate/create_product_lines.rb", /references :user, index: true/
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/resources :product_lines$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/product_lines_controller.rb" do |content|
+ assert_match(/class ProductLinesController < ApplicationController/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@product_lines = ProductLine\.all/, m)
+ end
+
+ assert_instance_method :show, content
+
+ assert_instance_method :new, content do |m|
+ assert_match(/@product_line = ProductLine\.new/, m)
+ end
+
+ assert_instance_method :edit, content
+
+ assert_instance_method :create, content do |m|
+ assert_match(/@product_line = ProductLine\.new\(product_line_params\)/, m)
+ assert_match(/@product_line\.save/, m)
+ end
+
+ assert_instance_method :update, content do |m|
+ assert_match(/@product_line\.update\(product_line_params\)/, m)
+ end
+
+ assert_instance_method :destroy, content do |m|
+ assert_match(/@product_line\.destroy/, m)
+ end
+
+ assert_instance_method :set_product_line, content do |m|
+ assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
+ end
+ end
+
+ assert_file "test/controllers/product_lines_controller_test.rb" do |test|
+ assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test)
+ assert_match(/post :create, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ end
+
+ # Views
+ assert_no_file "app/views/layouts/product_lines.html.erb"
+
+ %w(index show).each do |view|
+ assert_file "app/views/product_lines/#{view}.html.erb"
+ end
+
+ %w(edit new).each do |view|
+ assert_file "app/views/product_lines/#{view}.html.erb", /render 'form', product_line: @product_line/
+ end
+
+ assert_file "app/views/product_lines/_form.html.erb" do |test|
+ assert_match 'product_line', test
+ assert_no_match '@product_line', test
+ end
+
+ # Helpers
+ assert_file "app/helpers/product_lines_helper.rb"
+
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ assert_file "app/assets/javascripts/product_lines.js"
+ assert_file "app/assets/stylesheets/product_lines.css"
+ end
+
+ def test_api_scaffold_on_invoke
+ run_generator %w(product_line title:string product:belongs_to user:references --api --no-template-engine --no-helper --no-assets)
+
+ # Model
+ assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
+ assert_file "test/models/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/product_lines.yml"
+ assert_migration "db/migrate/create_product_lines.rb", /belongs_to :product, index: true/
+ assert_migration "db/migrate/create_product_lines.rb", /references :user, index: true/
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/resources :product_lines$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/product_lines_controller.rb" do |content|
+ assert_match(/class ProductLinesController < ApplicationController/, content)
+ assert_no_match(/respond_to/, content)
+
+ assert_match(/before_action :set_product_line, only: \[:show, :update, :destroy\]/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@product_lines = ProductLine\.all/, m)
+ assert_match(/render json: @product_lines/, m)
+ end
+
+ assert_instance_method :show, content do |m|
+ assert_match(/render json: @product_line/, m)
+ end
+
+ assert_instance_method :create, content do |m|
+ assert_match(/@product_line = ProductLine\.new\(product_line_params\)/, m)
+ assert_match(/@product_line\.save/, m)
+ assert_match(/@product_line\.errors/, m)
+ end
+
+ assert_instance_method :update, content do |m|
+ assert_match(/@product_line\.update\(product_line_params\)/, m)
+ assert_match(/@product_line\.errors/, m)
+ end
+
+ assert_instance_method :destroy, content do |m|
+ assert_match(/@product_line\.destroy/, m)
+ end
+ end
+
+ assert_file "test/controllers/product_lines_controller_test.rb" do |test|
+ assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test)
+ assert_match(/post :create, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_no_match(/assert_redirected_to/, test)
+ end
+
+ # Views
+ assert_no_file "app/views/layouts/product_lines.html.erb"
+
+ %w(index show new edit _form).each do |view|
+ assert_no_file "app/views/product_lines/#{view}.html.erb"
+ end
+
+ # Helpers
+ assert_no_file "app/helpers/product_lines_helper.rb"
+
+ # Assets
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_no_file "app/assets/javascripts/product_lines.js"
+ assert_no_file "app/assets/stylesheets/product_lines.css"
+ end
+
+ def test_functional_tests_without_attributes
+ run_generator ["product_line"]
+
+ assert_file "test/controllers/product_lines_controller_test.rb" do |content|
+ assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, content)
+ assert_match(/test "should get index"/, content)
+ assert_match(/post :create, params: \{ product_line: \{ \} \}/, content)
+ assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ \} \}/, content)
+ end
+ end
+
+ def test_scaffold_on_revoke
+ run_generator
+ run_generator ["product_line"], behavior: :revoke
+
+ # Model
+ assert_no_file "app/models/product_line.rb"
+ assert_no_file "test/models/product_line_test.rb"
+ assert_no_file "test/fixtures/product_lines.yml"
+ assert_no_migration "db/migrate/create_product_lines.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/resources :product_lines$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/product_lines_controller.rb"
+ assert_no_file "test/controllers/product_lines_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/product_lines"
+ assert_no_file "app/views/layouts/product_lines.html.erb"
+
+ # Helpers
+ assert_no_file "app/helpers/product_lines_helper.rb"
+
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css", /:visited/
+ assert_no_file "app/assets/javascripts/product_lines.js"
+ assert_no_file "app/assets/stylesheets/product_lines.css"
+ end
+
+ def test_scaffold_with_namespace_on_invoke
+ run_generator [ "admin/role", "name:string", "description:string" ]
+
+ # Model
+ assert_file "app/models/admin.rb", /module Admin/
+ assert_file "app/models/admin/role.rb", /class Admin::Role < ActiveRecord::Base/
+ assert_file "test/models/admin/role_test.rb", /class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/admin/roles.yml"
+ assert_migration "db/migrate/create_admin_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/admin/roles_controller.rb" do |content|
+ assert_match(/class Admin::RolesController < ApplicationController/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@admin_roles = Admin::Role\.all/, m)
+ end
+
+ assert_instance_method :show, content
+
+ assert_instance_method :new, content do |m|
+ assert_match(/@admin_role = Admin::Role\.new/, m)
+ end
+
+ assert_instance_method :edit, content
+
+ assert_instance_method :create, content do |m|
+ assert_match(/@admin_role = Admin::Role\.new\(admin_role_params\)/, m)
+ assert_match(/@admin_role\.save/, m)
+ end
+
+ assert_instance_method :update, content do |m|
+ assert_match(/@admin_role\.update\(admin_role_params\)/, m)
+ end
+
+ assert_instance_method :destroy, content do |m|
+ assert_match(/@admin_role\.destroy/, m)
+ end
+
+ assert_instance_method :set_admin_role, content do |m|
+ assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m)
+ end
+ end
+
+ assert_file "test/controllers/admin/roles_controller_test.rb",
+ /class Admin::RolesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(index edit new show _form).each do |view|
+ assert_file "app/views/admin/roles/#{view}.html.erb"
+ end
+ assert_no_file "app/views/layouts/admin/roles.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/admin/roles_helper.rb"
+
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css", /:visited/
+ assert_file "app/assets/javascripts/admin/roles.js"
+ assert_file "app/assets/stylesheets/admin/roles.css"
+ end
+
+ def test_scaffold_with_namespace_on_revoke
+ run_generator [ "admin/role", "name:string", "description:string" ]
+ run_generator [ "admin/role" ], :behavior => :revoke
+
+ # Model
+ assert_file "app/models/admin.rb" # ( should not be remove )
+ assert_no_file "app/models/admin/role.rb"
+ assert_no_file "test/models/admin/role_test.rb"
+ assert_no_file "test/fixtures/admin/roles.yml"
+ assert_no_migration "db/migrate/create_admin_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/namespace :admin do resources :roles end$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/admin/roles_controller.rb"
+ assert_no_file "test/controllers/admin/roles_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/admin/roles"
+ assert_no_file "app/views/layouts/admin/roles.html.erb"
+
+ # Helpers
+ assert_no_file "app/helpers/admin/roles_helper.rb"
+
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ assert_no_file "app/assets/javascripts/admin/roles.js"
+ assert_no_file "app/assets/stylesheets/admin/roles.css"
+ end
+
+ def test_scaffold_generator_on_revoke_does_not_mutilate_legacy_map_parameter
+ run_generator
+
+ # Add a |map| parameter to the routes block manually
+ route_path = File.expand_path("config/routes.rb", destination_root)
+ content = File.read(route_path).gsub(/\.routes\.draw do/) do |match|
+ "#{match} |map|"
+ end
+ File.open(route_path, "wb") { |file| file.write(content) }
+
+ run_generator ["product_line"], :behavior => :revoke
+
+ assert_file "config/routes.rb", /\.routes\.draw do\s*\|map\|\s*$/
+ end
+
+ def test_scaffold_generator_on_revoke_does_not_mutilate_routes
+ run_generator
+
+ route_path = File.expand_path("config/routes.rb", destination_root)
+ content = File.read(route_path)
+
+ # Remove all of the comments and blank lines from the routes file
+ content.gsub!(/^ \#.*\n/, '')
+ content.gsub!(/^\n/, '')
+
+ File.open(route_path, "wb") { |file| file.write(content) }
+ assert_file "config/routes.rb", /\.routes\.draw do\n resources :product_lines\nend\n\z/
+
+ run_generator ["product_line"], :behavior => :revoke
+
+ assert_file "config/routes.rb", /\.routes\.draw do\nend\n\z/
+ end
+
+ def test_scaffold_generator_ignores_commented_routes
+ run_generator ["product"]
+ assert_file "config/routes.rb", /\.routes\.draw do\n resources :products\n/
+ end
+
+ def test_scaffold_generator_no_assets_with_switch_no_assets
+ run_generator [ "posts", "--no-assets" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_no_file "app/assets/javascripts/posts.js"
+ assert_no_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_scaffold_generator_no_assets_with_switch_assets_false
+ run_generator [ "posts", "--assets=false" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_no_file "app/assets/javascripts/posts.js"
+ assert_no_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_scaffold_generator_no_scaffold_stylesheet_with_switch_no_scaffold_stylesheet
+ run_generator [ "posts", "--no-scaffold-stylesheet" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_scaffold_generator_no_scaffold_stylesheet_with_switch_scaffold_stylesheet_false
+ run_generator [ "posts", "--scaffold-stylesheet=false" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_scaffold_generator_with_switch_resource_route_false
+ run_generator [ "posts", "--resource-route=false" ]
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/resources :posts$/, route)
+ end
+ end
+
+ def test_scaffold_generator_no_helper_with_switch_no_helper
+ output = run_generator [ "posts", "--no-helper" ]
+
+ assert_no_match(/error/, output)
+ assert_no_file "app/helpers/posts_helper.rb"
+ end
+
+ def test_scaffold_generator_no_helper_with_switch_helper_false
+ output = run_generator [ "posts", "--helper=false" ]
+
+ assert_no_match(/error/, output)
+ assert_no_file "app/helpers/posts_helper.rb"
+ end
+
+ def test_scaffold_generator_no_stylesheets
+ run_generator [ "posts", "--no-stylesheets" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_file "app/assets/javascripts/posts.js"
+ assert_no_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_scaffold_generator_no_javascripts
+ run_generator [ "posts", "--no-javascripts" ]
+ assert_file "app/assets/stylesheets/scaffold.css"
+ assert_no_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_scaffold_generator_outputs_error_message_on_missing_attribute_type
+ run_generator ["post", "title", "body:text", "author"]
+
+ assert_migration "db/migrate/create_posts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.string :title/, up)
+ assert_match(/t\.text :body/, up)
+ assert_match(/t\.string :author/, up)
+ end
+ end
+ end
+
+ def test_scaffold_generator_belongs_to
+ run_generator ["account", "name", "currency:belongs_to"]
+
+ assert_file "app/models/account.rb", /belongs_to :currency/
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.belongs_to :currency/, up)
+ end
+ end
+
+ assert_file "app/controllers/accounts_controller.rb" do |content|
+ assert_instance_method :account_params, content do |m|
+ assert_match(/permit\(:name, :currency_id\)/, m)
+ end
+ 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)
+ end
+ end
+
+ def test_scaffold_generator_password_digest
+ run_generator ["user", "name", "password:digest"]
+
+ assert_file "app/models/user.rb", /has_secure_password/
+
+ assert_migration "db/migrate/create_users.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.string :password_digest/, up)
+ end
+ end
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_instance_method :user_params, content do |m|
+ assert_match(/permit\(:name, :password, :password_confirmation\)/, m)
+ end
+ 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)
+ end
+
+ assert_file "app/views/users/index.html.erb" do |content|
+ assert_no_match(/password/, content)
+ end
+
+ assert_file "app/views/users/show.html.erb" do |content|
+ assert_no_match(/password/, content)
+ end
+
+ assert_file "test/controllers/users_controller_test.rb" do |content|
+ assert_match(/password: 'secret'/, content)
+ assert_match(/password_confirmation: 'secret'/, content)
+ end
+
+ assert_file "test/fixtures/users.yml" do |content|
+ assert_match(/password_digest: <%= BCrypt::Password.create\('secret'\) %>/, content)
+ end
+ end
+
+ def test_scaffold_tests_pass_by_default_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 do
+ `bin/rails g scaffold User name:string age:integer;
+ bundle exec rake db:migrate`
+ end
+ assert_match(/8 runs, 13 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ end
+ end
+
+ def test_scaffold_tests_pass_by_default_inside_full_engine
+ Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --full` }
+
+ engine_path = File.join(destination_root, "bukkits")
+
+ Dir.chdir(engine_path) do
+ quietly do
+ `bin/rails g scaffold User name:string age:integer;
+ bundle exec rake db:migrate`
+ end
+ assert_match(/8 runs, 13 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ end
+ end
+
+ def test_scaffold_tests_pass_by_default_inside_api_mountable_engine
+ Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --mountable --api` }
+
+ engine_path = File.join(destination_root, "bukkits")
+
+ Dir.chdir(engine_path) do
+ quietly do
+ `bin/rails g scaffold User name:string age:integer;
+ bundle exec rake db:migrate`
+ end
+ assert_match(/6 runs, 8 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ end
+ end
+
+ def test_scaffold_tests_pass_by_default_inside_api_full_engine
+ Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --full --api` }
+
+ engine_path = File.join(destination_root, "bukkits")
+
+ Dir.chdir(engine_path) do
+ quietly do
+ `bin/rails g scaffold User name:string age:integer;
+ bundle exec rake db:migrate`
+ end
+ assert_match(/6 runs, 8 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`)
+ end
+ end
+end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
new file mode 100644
index 0000000000..acb78ec888
--- /dev/null
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -0,0 +1,159 @@
+#
+# Tests, setup, and teardown common to the application and plugin generator suites.
+#
+module SharedGeneratorTests
+ def setup
+ Rails.application = TestApp::Application
+ super
+ Rails::Generators::AppGenerator.instance_variable_set('@desc', nil)
+
+ Kernel::silence_warnings do
+ Thor::Base.shell.send(:attr_accessor, :always_force)
+ @shell = Thor::Base.shell.new
+ @shell.send(:always_force=, true)
+ end
+ end
+
+ def teardown
+ super
+ Rails::Generators::AppGenerator.instance_variable_set('@desc', nil)
+ Rails.application = TestApp::Application.instance
+ end
+
+ def test_skeleton_is_created
+ run_generator
+
+ default_files.each { |path| assert_file path }
+ end
+
+ def assert_generates_with_bundler(options = {})
+ generator([destination_root], options)
+
+ command_check = -> command do
+ @install_called ||= 0
+
+ case command
+ when 'install'
+ @install_called += 1
+ assert_equal 1, @install_called, "install expected to be called once, but was called #{@install_called} times"
+ when 'exec spring binstub --all'
+ # Called when running tests with spring, let through unscathed.
+ end
+ end
+
+ generator.stub :bundle_command, command_check do
+ quietly { generator.invoke_all }
+ end
+ end
+
+ def test_generation_runs_bundle_install
+ assert_generates_with_bundler
+ end
+
+ def test_plugin_new_generate_pretend
+ run_generator ["testapp", "--pretend"]
+ default_files.each{ |path| assert_no_file File.join("testapp",path) }
+ end
+
+ def test_invalid_database_option_raises_an_error
+ content = capture(:stderr){ run_generator([destination_root, "-d", "unknown"]) }
+ assert_match(/Invalid value for \-\-database option/, content)
+ end
+
+ def test_test_files_are_skipped_if_required
+ run_generator [destination_root, "--skip-test"]
+ assert_no_file "test"
+ end
+
+ def test_name_collision_raises_an_error
+ reserved_words = %w[application destroy plugin runner test]
+ reserved_words.each do |reserved|
+ content = capture(:stderr){ run_generator [File.join(destination_root, reserved)] }
+ assert_match(/Invalid \w+ name #{reserved}. Please give a name which does not match one of the reserved rails words: application, destroy, plugin, runner, test\n/, content)
+ end
+ end
+
+ def test_name_raises_an_error_if_name_already_used_constant
+ %w{ String Hash Class Module Set Symbol }.each do |ruby_class|
+ content = capture(:stderr){ run_generator [File.join(destination_root, ruby_class)] }
+ assert_match(/Invalid \w+ name #{ruby_class}, constant #{ruby_class} is already in use. Please choose another \w+ name.\n/, content)
+ end
+ end
+
+ def test_shebang_is_added_to_rails_file
+ run_generator [destination_root, "--ruby", "foo/bar/baz", "--full"]
+ assert_file "bin/rails", /#!foo\/bar\/baz/
+ end
+
+ def test_shebang_when_is_the_same_as_default_use_env
+ run_generator [destination_root, "--ruby", Thor::Util.ruby_command, "--full"]
+ assert_file "bin/rails", /#!\/usr\/bin\/env/
+ end
+
+ def test_template_raises_an_error_with_invalid_path
+ quietly do
+ content = capture(:stderr){ run_generator([destination_root, "-m", "non/existent/path"]) }
+
+ assert_match(/The template \[.*\] could not be loaded/, content)
+ assert_match(/non\/existent\/path/, content)
+ end
+ end
+
+ def test_template_is_executed_when_supplied_an_https_path
+ path = "https://gist.github.com/josevalim/103208/raw/"
+ template = %{ say "It works!" }
+ template.instance_eval "def read; self; end" # Make the string respond to read
+
+ check_open = -> *args do
+ assert_equal [ path, 'Accept' => 'application/x-thor-template' ], args
+ template
+ end
+
+ generator([destination_root], template: path).stub(:open, check_open, template) do
+ quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) }
+ end
+ end
+
+ def test_dev_option
+ assert_generates_with_bundler dev: true
+ rails_path = File.expand_path('../../..', Rails.root)
+ assert_file 'Gemfile', /^gem\s+["']rails["'],\s+path:\s+["']#{Regexp.escape(rails_path)}["']$/
+ end
+
+ def test_edge_option
+ assert_generates_with_bundler edge: true
+ assert_file 'Gemfile', %r{^gem\s+["']rails["'],\s+github:\s+["']#{Regexp.escape("rails/rails")}["']$}
+ end
+
+ def test_skip_gemfile
+ assert_not_called(generator([destination_root], skip_gemfile: true), :bundle_command) do
+ quietly { generator.invoke_all }
+ assert_no_file 'Gemfile'
+ end
+ end
+
+ def test_skip_bundle
+ assert_not_called(generator([destination_root], skip_bundle: true), :bundle_command) do
+ quietly { generator.invoke_all }
+ # skip_bundle is only about running bundle install, ensure the Gemfile is still
+ # generated.
+ assert_file 'Gemfile'
+ end
+ end
+
+ def test_skip_git
+ run_generator [destination_root, '--skip-git', '--full']
+ assert_no_file('.gitignore')
+ assert_file('app/mailers/.keep')
+ end
+
+ def test_skip_keeps
+ run_generator [destination_root, '--skip-keeps', '--full']
+
+ assert_file '.gitignore' do |content|
+ assert_no_match(/\.keep/, content)
+ end
+
+ assert_no_file('app/mailers/.keep')
+ end
+end
diff --git a/railties/test/generators/task_generator_test.rb b/railties/test/generators/task_generator_test.rb
new file mode 100644
index 0000000000..d5bd44b9db
--- /dev/null
+++ b/railties/test/generators/task_generator_test.rb
@@ -0,0 +1,24 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/task/task_generator'
+
+class TaskGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(feeds foo bar)
+
+ def test_task_is_created
+ run_generator
+ assert_file "lib/tasks/feeds.rake" do |content|
+ assert_match(/namespace :feeds/, content)
+ assert_match(/task foo:/, content)
+ assert_match(/task bar:/, content)
+ end
+ end
+
+ def test_task_on_revoke
+ task_path = 'lib/tasks/feeds.rake'
+ run_generator
+ assert_file task_path
+ run_generator ['feeds'], behavior: :revoke
+ assert_no_file task_path
+ end
+end
diff --git a/railties/test/generators/test_runner_in_engine_test.rb b/railties/test/generators/test_runner_in_engine_test.rb
new file mode 100644
index 0000000000..641c5d0835
--- /dev/null
+++ b/railties/test/generators/test_runner_in_engine_test.rb
@@ -0,0 +1,31 @@
+require 'generators/plugin_test_helper'
+
+class TestRunnerInEngineTest < ActiveSupport::TestCase
+ include PluginTestHelper
+
+ def setup
+ @destination_root = Dir.mktmpdir('bukkits')
+ Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --full --skip-bundle` }
+ plugin_file 'test/dummy/db/schema.rb', ''
+ end
+
+ def teardown
+ FileUtils.rm_rf(@destination_root)
+ end
+
+ def test_rerun_snippet_is_relative_path
+ create_test_file 'post', pass: false
+
+ output = run_test_command('test/post_test.rb')
+ assert_match %r{Running:\n\nPostTest\nF\n\nwups!\n\nbin/rails test test/post_test.rb:6}, output
+ end
+
+ private
+ def plugin_path
+ "#{@destination_root}/bukkits"
+ end
+
+ def run_test_command(arguments)
+ Dir.chdir(plugin_path) { `bin/rails test #{arguments}` }
+ end
+end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
new file mode 100644
index 0000000000..291415858c
--- /dev/null
+++ b/railties/test/generators_test.rb
@@ -0,0 +1,246 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/model/model_generator'
+require 'rails/generators/test_unit/model/model_generator'
+
+class GeneratorsTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ def setup
+ @path = File.expand_path("lib", Rails.root)
+ $LOAD_PATH.unshift(@path)
+ end
+
+ def teardown
+ $LOAD_PATH.delete(@path)
+ end
+
+ def test_simple_invoke
+ assert File.exist?(File.join(@path, 'generators', 'model_generator.rb'))
+ assert_called_with(TestUnit::Generators::ModelGenerator, :start, [["Account"], {}]) do
+ Rails::Generators.invoke("test_unit:model", ["Account"])
+ end
+ end
+
+ def test_invoke_when_generator_is_not_found
+ name = :unknown
+ output = capture(:stdout){ Rails::Generators.invoke name }
+ assert_match "Could not find generator '#{name}'", output
+ assert_match "`rails generate --help`", output
+ end
+
+ def test_generator_suggestions
+ name = :migrationz
+ output = capture(:stdout){ Rails::Generators.invoke name }
+ assert_match "Maybe you meant 'migration'", output
+ end
+
+ def test_generator_multiple_suggestions
+ name = :tas
+ output = capture(:stdout){ Rails::Generators.invoke name }
+ assert_match "Maybe you meant 'task', 'job' or", output
+ end
+
+ def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments
+ output = capture(:stdout){ Rails::Generators.invoke :model, [] }
+ assert_match(/Description:/, output)
+ end
+
+ def test_should_give_higher_preference_to_rails_generators
+ assert File.exist?(File.join(@path, 'generators', 'model_generator.rb'))
+ assert_called_with(Rails::Generators::ModelGenerator, :start, [["Account"], {}]) do
+ warnings = capture(:stderr){ Rails::Generators.invoke :model, ["Account"] }
+ assert warnings.empty?
+ end
+ end
+
+ def test_invoke_with_default_values
+ assert_called_with(Rails::Generators::ModelGenerator, :start, [["Account"], {}]) do
+ Rails::Generators.invoke :model, ["Account"]
+ end
+ end
+
+ def test_invoke_with_config_values
+ assert_called_with(Rails::Generators::ModelGenerator, :start, [["Account"], behavior: :skip]) do
+ Rails::Generators.invoke :model, ["Account"], behavior: :skip
+ end
+ end
+
+ def test_find_by_namespace
+ klass = Rails::Generators.find_by_namespace("rails:model")
+ assert klass
+ assert_equal "rails:model", klass.namespace
+ end
+
+ def test_find_by_namespace_with_base
+ klass = Rails::Generators.find_by_namespace(:model, :rails)
+ assert klass
+ assert_equal "rails:model", klass.namespace
+ end
+
+ def test_find_by_namespace_with_context
+ klass = Rails::Generators.find_by_namespace(:test_unit, nil, :model)
+ assert klass
+ assert_equal "test_unit:model", klass.namespace
+ end
+
+ def test_find_by_namespace_with_generator_on_root
+ klass = Rails::Generators.find_by_namespace(:fixjour)
+ assert klass
+ assert_equal "fixjour", klass.namespace
+ end
+
+ def test_find_by_namespace_in_subfolder
+ klass = Rails::Generators.find_by_namespace(:fixjour, :active_record)
+ assert klass
+ assert_equal "active_record:fixjour", klass.namespace
+ end
+
+ def test_find_by_namespace_with_duplicated_name
+ klass = Rails::Generators.find_by_namespace(:foobar)
+ assert klass
+ assert_equal "foobar:foobar", klass.namespace
+ end
+
+ def test_find_by_namespace_without_base_or_context_looks_into_rails_namespace
+ assert Rails::Generators.find_by_namespace(:model)
+ end
+
+ def test_invoke_with_nested_namespaces
+ model_generator = Minitest::Mock.new
+ model_generator.expect(:start, nil, [["Account"], {}])
+ assert_called_with(Rails::Generators, :find_by_namespace, ['namespace', 'my:awesome'], returns: model_generator) do
+ Rails::Generators.invoke 'my:awesome:namespace', ["Account"]
+ end
+ model_generator.verify
+ end
+
+ def test_rails_generators_help_with_builtin_information
+ output = capture(:stdout){ Rails::Generators.help }
+ assert_match(/Rails:/, output)
+ assert_match(/^ model$/, output)
+ assert_match(/^ scaffold_controller$/, output)
+ assert_no_match(/^ app$/, output)
+ end
+
+ def test_rails_generators_help_does_not_include_app_nor_plugin_new
+ output = capture(:stdout){ Rails::Generators.help }
+ assert_no_match(/app/, output)
+ assert_no_match(/[^:]plugin/, output)
+ end
+
+ def test_rails_generators_with_others_information
+ output = capture(:stdout){ Rails::Generators.help }
+ assert_match(/Fixjour:/, output)
+ assert_match(/^ fixjour$/, output)
+ end
+
+ def test_rails_generators_does_not_show_active_record_hooks
+ output = capture(:stdout){ Rails::Generators.help }
+ assert_match(/ActiveRecord:/, output)
+ assert_match(/^ active_record:fixjour$/, output)
+ end
+
+ def test_default_banner_should_show_generator_namespace
+ klass = Rails::Generators.find_by_namespace(:foobar)
+ assert_match(/^rails generate foobar:foobar/, klass.banner)
+ end
+
+ def test_default_banner_should_not_show_rails_generator_namespace
+ klass = Rails::Generators.find_by_namespace(:model)
+ assert_match(/^rails generate model/, klass.banner)
+ end
+
+ def test_no_color_sets_proper_shell
+ Rails::Generators.no_color!
+ assert_equal Thor::Shell::Basic, Thor::Base.shell
+ ensure
+ Thor::Base.shell = Thor::Shell::Color
+ end
+
+ def test_fallbacks_for_generators_on_find_by_namespace
+ Rails::Generators.fallbacks[:remarkable] = :test_unit
+ klass = Rails::Generators.find_by_namespace(:plugin, :remarkable)
+ assert klass
+ assert_equal "test_unit:plugin", klass.namespace
+ ensure
+ Rails::Generators.fallbacks.delete(:remarkable)
+ end
+
+ def test_fallbacks_for_generators_on_find_by_namespace_with_context
+ Rails::Generators.fallbacks[:remarkable] = :test_unit
+ klass = Rails::Generators.find_by_namespace(:remarkable, :rails, :plugin)
+ assert klass
+ assert_equal "test_unit:plugin", klass.namespace
+ ensure
+ Rails::Generators.fallbacks.delete(:remarkable)
+ end
+
+ def test_fallbacks_for_generators_on_invoke
+ Rails::Generators.fallbacks[:shoulda] = :test_unit
+ assert_called_with(TestUnit::Generators::ModelGenerator, :start, [["Account"], {}]) do
+ Rails::Generators.invoke "shoulda:model", ["Account"]
+ end
+ ensure
+ Rails::Generators.fallbacks.delete(:shoulda)
+ end
+
+ def test_nested_fallbacks_for_generators
+ Rails::Generators.fallbacks[:shoulda] = :test_unit
+ Rails::Generators.fallbacks[:super_shoulda] = :shoulda
+ assert_called_with(TestUnit::Generators::ModelGenerator, :start, [["Account"], {}]) do
+ Rails::Generators.invoke "super_shoulda:model", ["Account"]
+ end
+ ensure
+ Rails::Generators.fallbacks.delete(:shoulda)
+ Rails::Generators.fallbacks.delete(:super_shoulda)
+ end
+
+ def test_developer_options_are_overwritten_by_user_options
+ Rails::Generators.options[:with_options] = { generate: false }
+
+ self.class.class_eval(<<-end_eval, __FILE__, __LINE__ + 1)
+ class WithOptionsGenerator < Rails::Generators::Base
+ class_option :generate, :default => true
+ end
+ end_eval
+
+ assert_equal false, WithOptionsGenerator.class_options[:generate].default
+ ensure
+ Rails::Generators.subclasses.delete(WithOptionsGenerator)
+ end
+
+ def test_rails_root_templates
+ template = File.join(Rails.root, "lib", "templates", "active_record", "model", "model.rb")
+
+ # Create template
+ mkdir_p(File.dirname(template))
+ File.open(template, 'w'){ |f| f.write "empty" }
+
+ capture(:stdout) do
+ Rails::Generators.invoke :model, ["user"], destination_root: destination_root
+ end
+
+ assert_file "app/models/user.rb" do |content|
+ assert_equal "empty", content
+ end
+ ensure
+ rm_rf File.dirname(template)
+ end
+
+ def test_source_paths_for_not_namespaced_generators
+ mspec = Rails::Generators.find_by_namespace :fixjour
+ assert mspec.source_paths.include?(File.join(Rails.root, "lib", "templates", "fixjour"))
+ end
+
+ def test_usage_with_embedded_ruby
+ require File.expand_path("fixtures/lib/generators/usage_template/usage_template_generator", File.dirname(__FILE__))
+ output = capture(:stdout) { Rails::Generators.invoke :usage_template, ['--help'] }
+ assert_match(/:: 2 ::/, output)
+ end
+
+ def test_hide_namespace
+ assert !Rails::Generators.hidden_namespaces.include?("special:namespace")
+ Rails::Generators.hide_namespace("special:namespace")
+ assert Rails::Generators.hidden_namespaces.include?("special:namespace")
+ end
+end
diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb
new file mode 100644
index 0000000000..ed9573453b
--- /dev/null
+++ b/railties/test/initializable_test.rb
@@ -0,0 +1,230 @@
+require 'abstract_unit'
+require 'rails/initializable'
+
+module InitializableTests
+
+ class Foo
+ include Rails::Initializable
+ attr_accessor :foo, :bar
+
+ initializer :start do
+ @foo ||= 0
+ @foo += 1
+ end
+ end
+
+ class Bar < Foo
+ initializer :bar do
+ @bar ||= 0
+ @bar += 1
+ end
+ end
+
+ class Parent
+ include Rails::Initializable
+
+ initializer :one do
+ $arr << 1
+ end
+
+ initializer :two do
+ $arr << 2
+ end
+ end
+
+ class Child < Parent
+ include Rails::Initializable
+
+ initializer :three, before: :one do
+ $arr << 3
+ end
+
+ initializer :four, after: :one, before: :two do
+ $arr << 4
+ end
+ end
+
+ class Parent
+ initializer :five, before: :one do
+ $arr << 5
+ end
+ end
+
+ class Instance
+ include Rails::Initializable
+
+ initializer :one, group: :assets do
+ $arr << 1
+ end
+
+ initializer :two do
+ $arr << 2
+ end
+
+ initializer :three, group: :all do
+ $arr << 3
+ end
+
+ initializer :four do
+ $arr << 4
+ end
+ end
+
+ class WithArgs
+ include Rails::Initializable
+
+ initializer :foo do |arg|
+ $with_arg = arg
+ end
+ end
+
+ class OverriddenInitializer
+ class MoreInitializers
+ include Rails::Initializable
+
+ initializer :startup, before: :last do
+ $arr << 3
+ end
+
+ initializer :terminate, after: :first, before: :startup do
+ $arr << two
+ end
+
+ def two
+ 2
+ end
+ end
+
+ include Rails::Initializable
+
+ initializer :first do
+ $arr << 1
+ end
+
+ initializer :last do
+ $arr << 4
+ end
+
+ def self.initializers
+ super + MoreInitializers.new.initializers
+ end
+ end
+
+ module Interdependent
+ class PluginA
+ include Rails::Initializable
+
+ initializer "plugin_a.startup" do
+ $arr << 1
+ end
+
+ initializer "plugin_a.terminate" do
+ $arr << 4
+ end
+ end
+
+ class PluginB
+ include Rails::Initializable
+
+ initializer "plugin_b.startup", after: "plugin_a.startup" do
+ $arr << 2
+ end
+
+ initializer "plugin_b.terminate", before: "plugin_a.terminate" do
+ $arr << 3
+ end
+ end
+
+ class Application
+ include Rails::Initializable
+ def self.initializers
+ PluginB.initializers + PluginA.initializers
+ end
+ end
+ end
+
+ class Basic < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ test "initializers run" do
+ foo = Foo.new
+ foo.run_initializers
+ assert_equal 1, foo.foo
+ end
+
+ test "initializers are inherited" do
+ bar = Bar.new
+ bar.run_initializers
+ assert_equal [1, 1], [bar.foo, bar.bar]
+ end
+
+ test "initializers only get run once" do
+ foo = Foo.new
+ foo.run_initializers
+ foo.run_initializers
+ assert_equal 1, foo.foo
+ end
+
+ test "creating initializer without a block raises an error" do
+ assert_raise(ArgumentError) do
+ Class.new do
+ include Rails::Initializable
+
+ initializer :foo
+ end
+ end
+ end
+ end
+
+ class BeforeAfter < ActiveSupport::TestCase
+ test "running on parent" do
+ $arr = []
+ Parent.new.run_initializers
+ assert_equal [5, 1, 2], $arr
+ end
+
+ test "running on child" do
+ $arr = []
+ Child.new.run_initializers
+ assert_equal [5, 3, 1, 4, 2], $arr
+ end
+
+ test "handles dependencies introduced before all initializers are loaded" do
+ $arr = []
+ Interdependent::Application.new.run_initializers
+ assert_equal [1, 2, 3, 4], $arr
+ end
+ end
+
+ class InstanceTest < ActiveSupport::TestCase
+ test "running locals" do
+ $arr = []
+ instance = Instance.new
+ instance.run_initializers
+ assert_equal [2, 3, 4], $arr
+ end
+
+ test "running locals with groups" do
+ $arr = []
+ instance = Instance.new
+ instance.run_initializers(:assets)
+ assert_equal [1, 3], $arr
+ end
+ end
+
+ class WithArgsTest < ActiveSupport::TestCase
+ test "running initializers with args" do
+ $with_arg = nil
+ WithArgs.new.run_initializers(:default, 'foo')
+ assert_equal 'foo', $with_arg
+ end
+ end
+
+ class OverriddenInitializerTest < ActiveSupport::TestCase
+ test "merges in the initializers from the parent in the right order" do
+ $arr = []
+ OverriddenInitializer.new.run_initializers
+ assert_equal [1, 2, 3, 4], $arr
+ end
+ end
+end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
new file mode 100644
index 0000000000..df3c2ca66d
--- /dev/null
+++ b/railties/test/isolation/abstract_unit.rb
@@ -0,0 +1,339 @@
+# Note:
+# It is important to keep this file as light as possible
+# the goal for tests that require this is to test booting up
+# rails from an empty state, so anything added here could
+# hide potential failures
+#
+# It is also good to know what is the bare minimum to get
+# Rails booted up.
+require 'fileutils'
+
+require 'bundler/setup' unless defined?(Bundler)
+require 'active_support'
+require 'active_support/testing/autorun'
+require 'active_support/testing/stream'
+require 'active_support/test_case'
+
+RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..")
+
+# These files do not require any others and are needed
+# to run the tests
+require "active_support/testing/isolation"
+require "active_support/core_ext/kernel/reporting"
+require 'tmpdir'
+
+module TestHelpers
+ module Paths
+ def app_template_path
+ File.join Dir.tmpdir, 'app_template'
+ end
+
+ def tmp_path(*args)
+ @tmp_path ||= File.realpath(Dir.mktmpdir)
+ File.join(@tmp_path, *args)
+ end
+
+ def app_path(*args)
+ tmp_path(*%w[app] + args)
+ end
+
+ def framework_path
+ RAILS_FRAMEWORK_ROOT
+ end
+
+ def rails_root
+ app_path
+ end
+ end
+
+ module Rack
+ def app(env = "production")
+ old_env = ENV["RAILS_ENV"]
+ @app ||= begin
+ ENV["RAILS_ENV"] = env
+
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require "#{app_path}/config/environment"
+ end
+
+ Rails.application
+ end
+ ensure
+ ENV["RAILS_ENV"] = old_env
+ end
+
+ def extract_body(response)
+ "".tap do |body|
+ response[2].each {|chunk| body << chunk }
+ end
+ end
+
+ def get(path)
+ @app.call(::Rack::MockRequest.env_for(path))
+ end
+
+ def assert_welcome(resp)
+ assert_equal 200, resp[0]
+ assert_match 'text/html', resp[1]["Content-Type"]
+ assert_match 'charset=utf-8', resp[1]["Content-Type"]
+ assert extract_body(resp).match(/Welcome aboard/)
+ end
+
+ def assert_success(resp)
+ assert_equal 202, resp[0]
+ end
+
+ def assert_missing(resp)
+ assert_equal 404, resp[0]
+ end
+
+ def assert_header(key, value, resp)
+ assert_equal value, resp[1][key.to_s]
+ end
+
+ def assert_body(expected, resp)
+ assert_equal expected, extract_body(resp)
+ end
+ end
+
+ module Generation
+ # Build an application by invoking the generator and going through the whole stack.
+ def build_app(options = {})
+ @prev_rails_env = ENV['RAILS_ENV']
+ ENV['RAILS_ENV'] = "development"
+ ENV['SECRET_KEY_BASE'] ||= SecureRandom.hex(16)
+
+ FileUtils.rm_rf(app_path)
+ FileUtils.cp_r(app_template_path, app_path)
+
+ # Delete the initializers unless requested
+ unless options[:initializers]
+ Dir["#{app_path}/config/initializers/*.rb"].each do |initializer|
+ File.delete(initializer)
+ end
+ end
+
+ gemfile_path = "#{app_path}/Gemfile"
+ if options[:gemfile].blank? && File.exist?(gemfile_path)
+ File.delete gemfile_path
+ end
+
+ routes = File.read("#{app_path}/config/routes.rb")
+ if routes =~ /(\n\s*end\s*)\Z/
+ File.open("#{app_path}/config/routes.rb", 'w') do |f|
+ f.puts $` + "\nmatch ':controller(/:action(/:id))(.:format)', via: :all\n" + $1
+ end
+ end
+
+ File.open("#{app_path}/config/database.yml", "w") do |f|
+ f.puts <<-YAML
+ default: &default
+ adapter: sqlite3
+ pool: 5
+ timeout: 5000
+ development:
+ <<: *default
+ database: db/development.sqlite3
+ test:
+ <<: *default
+ database: db/test.sqlite3
+ production:
+ <<: *default
+ database: db/production.sqlite3
+ YAML
+ end
+
+ add_to_config <<-RUBY
+ config.eager_load = false
+ config.session_store :cookie_store, key: "_myapp_session"
+ config.active_support.deprecation = :log
+ config.active_support.test_order = :random
+ config.action_controller.allow_forgery_protection = false
+ config.log_level = :info
+ RUBY
+ end
+
+ def teardown_app
+ ENV['RAILS_ENV'] = @prev_rails_env if @prev_rails_env
+ end
+
+ # Make a very basic app, without creating the whole directory structure.
+ # This is faster and simpler than the method above.
+ def make_basic_app
+ require "rails"
+ require "action_controller/railtie"
+ require "action_view/railtie"
+ require 'action_dispatch/middleware/flash'
+
+ @app = Class.new(Rails::Application)
+ @app.config.eager_load = false
+ @app.secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4"
+ @app.config.session_store :cookie_store, key: "_myapp_session"
+ @app.config.active_support.deprecation = :log
+ @app.config.active_support.test_order = :random
+ @app.config.log_level = :info
+
+ yield @app if block_given?
+ @app.initialize!
+
+ @app.routes.draw do
+ get "/" => "omg#index"
+ end
+
+ require 'rack/test'
+ extend ::Rack::Test::Methods
+ end
+
+ def simple_controller
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render text: "foo"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get ':controller(/:action)'
+ end
+ RUBY
+ end
+
+ class Bukkit
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ end
+
+ def write(file, string)
+ path = "#{@path}/#{file}"
+ FileUtils.mkdir_p(File.dirname(path))
+ File.open(path, "w") {|f| f.puts string }
+ end
+
+ def delete(file)
+ File.delete("#{@path}/#{file}")
+ end
+ end
+
+ def engine(name)
+ dir = "#{app_path}/random/#{name}"
+ FileUtils.mkdir_p(dir)
+
+ app = File.readlines("#{app_path}/config/application.rb")
+ app.insert(2, "$:.unshift(\"#{dir}/lib\")")
+ app.insert(3, "require #{name.inspect}")
+
+ File.open("#{app_path}/config/application.rb", 'r+') do |f|
+ f.puts app
+ end
+
+ Bukkit.new(dir).tap do |bukkit|
+ yield bukkit if block_given?
+ end
+ end
+
+ def script(script)
+ Dir.chdir(app_path) do
+ `#{Gem.ruby} #{app_path}/bin/rails #{script}`
+ end
+ end
+
+ def add_to_top_of_config(str)
+ environment = File.read("#{app_path}/config/application.rb")
+ if environment =~ /(Rails::Application\s*)/
+ File.open("#{app_path}/config/application.rb", 'w') do |f|
+ f.puts $` + $1 + "\n#{str}\n" + $'
+ end
+ end
+ end
+
+ def add_to_config(str)
+ environment = File.read("#{app_path}/config/application.rb")
+ 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
+ end
+ end
+
+ def add_to_env_config(env, str)
+ environment = File.read("#{app_path}/config/environments/#{env}.rb")
+ 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
+ end
+ end
+
+ def remove_from_config(str)
+ file = "#{app_path}/config/application.rb"
+ contents = File.read(file)
+ contents.sub!(/#{str}/, "")
+ File.open(file, "w+") { |f| f.puts contents }
+ end
+
+ def app_file(path, contents, mode = 'w')
+ FileUtils.mkdir_p File.dirname("#{app_path}/#{path}")
+ File.open("#{app_path}/#{path}", mode) do |f|
+ f.puts contents
+ end
+ end
+
+ def remove_file(path)
+ FileUtils.rm_rf "#{app_path}/#{path}"
+ end
+
+ def controller(name, contents)
+ app_file("app/controllers/#{name}_controller.rb", contents)
+ end
+
+ def use_frameworks(arr)
+ to_remove = [:actionmailer, :activerecord] - arr
+
+ if to_remove.include?(:activerecord)
+ remove_from_config 'config.active_record.*'
+ end
+
+ $:.reject! {|path| path =~ %r'/(#{to_remove.join('|')})/' }
+ end
+
+ def boot_rails
+ # FIXME: shush Sass warning spam, not relevant to testing Railties
+ Kernel.silence_warnings do
+ require File.expand_path('../../../../load_paths', __FILE__)
+ end
+ end
+ end
+end
+
+class ActiveSupport::TestCase
+ include TestHelpers::Paths
+ include TestHelpers::Rack
+ include TestHelpers::Generation
+ include ActiveSupport::Testing::Stream
+
+ self.test_order = :sorted
+
+end
+
+# Create a scope and build a fixture rails app
+Module.new do
+ extend TestHelpers::Paths
+
+ # Build a rails app
+ FileUtils.rm_rf(app_template_path)
+ FileUtils.mkdir(app_template_path)
+
+ environment = File.expand_path('../../../../load_paths', __FILE__)
+ require_environment = "-r #{environment}"
+
+ `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-gemfile --no-rc`
+ File.open("#{app_template_path}/config/boot.rb", 'w') do |f|
+ f.puts "require '#{environment}'"
+ f.puts "require 'rails/all'"
+ end
+end unless defined?(RAILS_ISOLATED_ENGINE)
diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb
new file mode 100644
index 0000000000..a16adc72a6
--- /dev/null
+++ b/railties/test/path_generation_test.rb
@@ -0,0 +1,83 @@
+require 'abstract_unit'
+require 'active_support/core_ext/object/with_options'
+require 'active_support/core_ext/object/json'
+
+class PathGenerationTest < ActiveSupport::TestCase
+ attr_reader :app
+
+ class TestSet < ActionDispatch::Routing::RouteSet
+ def initialize(block)
+ @block = block
+ super()
+ end
+
+ class Request < DelegateClass(ActionDispatch::Request)
+ def initialize(target, url_helpers, block)
+ super(target)
+ @url_helpers = url_helpers
+ @block = block
+ end
+
+ def controller_class
+ url_helpers = @url_helpers
+ block = @block
+ Class.new(ActionController::Base) {
+ include url_helpers
+ define_method(:process) { |name| block.call(self) }
+ def to_a; [200, {}, []]; end
+ }
+ end
+ end
+
+ def make_request(env)
+ Request.new super, self.url_helpers, @block
+ end
+ end
+
+ def send_request(uri_or_host, method, path, script_name = nil)
+ host = uri_or_host.host unless path
+ path ||= uri_or_host.path
+
+ params = {'PATH_INFO' => path,
+ 'REQUEST_METHOD' => method,
+ 'HTTP_HOST' => host }
+
+ params['SCRIPT_NAME'] = script_name if script_name
+
+ status, headers, body = app.call(params)
+ new_body = []
+ body.each { |part| new_body << part }
+ body.close if body.respond_to? :close
+ [status, headers, new_body]
+ end
+
+ def test_original_script_name
+ original_logger = Rails.logger
+ Rails.logger = Logger.new nil
+
+ app = Class.new(Rails::Application) {
+ attr_accessor :controller
+ def initialize
+ super
+ app = self
+ @routes = TestSet.new ->(c) { app.controller = c }
+ secrets.secret_key_base = "foo"
+ secrets.secret_token = "foo"
+ end
+ def app; routes; end
+ }
+
+ @app = app
+ app.routes.draw { resource :blogs }
+
+ url = URI("http://example.org/blogs")
+
+ send_request(url, 'GET', nil, '/FOO')
+ assert_equal '/FOO/blogs', app.instance.controller.blogs_path
+
+ send_request(url, 'GET', nil)
+ assert_equal '/blogs', app.instance.controller.blogs_path
+ ensure
+ Rails.logger = original_logger
+ end
+end
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
new file mode 100644
index 0000000000..96b54c7264
--- /dev/null
+++ b/railties/test/paths_test.rb
@@ -0,0 +1,276 @@
+require 'abstract_unit'
+require 'rails/paths'
+require 'minitest/mock'
+
+class PathsTest < ActiveSupport::TestCase
+ def setup
+ @root = Rails::Paths::Root.new("/foo/bar")
+ end
+
+ test "the paths object is initialized with the root path" do
+ root = Rails::Paths::Root.new("/fiz/baz")
+ assert_equal "/fiz/baz", root.path
+ end
+
+ test "the paths object can be initialized with nil" do
+ assert_nothing_raised do
+ Rails::Paths::Root.new(nil)
+ end
+ end
+
+ test "a paths object initialized with nil can be updated" do
+ root = Rails::Paths::Root.new(nil)
+ root.add "app"
+ root.path = "/root"
+ assert_equal ["app"], root["app"].to_ary
+ assert_equal ["/root/app"], root["app"].to_a
+ end
+
+ test "creating a root level path" do
+ @root.add "app"
+ assert_equal ["/foo/bar/app"], @root["app"].to_a
+ end
+
+ test "creating a root level path with options" do
+ @root.add "app", with: "/foo/bar"
+ assert_equal ["/foo/bar"], @root["app"].to_a
+ end
+
+ test "raises exception if root path never set" do
+ root = Rails::Paths::Root.new(nil)
+ root.add "app"
+ assert_raises RuntimeError do
+ root["app"].to_a
+ end
+ end
+
+ test "creating a child level path" do
+ @root.add "app"
+ @root.add "app/models"
+ assert_equal ["/foo/bar/app/models"], @root["app/models"].to_a
+ end
+
+ test "creating a child level path with option" do
+ @root.add "app"
+ @root.add "app/models", with: "/foo/bar/baz"
+ assert_equal ["/foo/bar/baz"], @root["app/models"].to_a
+ end
+
+ test "child level paths are relative from the root" do
+ @root.add "app"
+ @root.add "app/models", with: "baz"
+ assert_equal ["/foo/bar/baz"], @root["app/models"].to_a
+ end
+
+ test "absolute current path" do
+ @root.add "config"
+ @root.add "config/locales"
+
+ assert_equal "/foo/bar/config/locales", @root["config/locales"].absolute_current
+ end
+
+ test "adding multiple physical paths as an array" do
+ @root.add "app", with: ["/app", "/app2"]
+ assert_equal ["/app", "/app2"], @root["app"].to_a
+ end
+
+ test "adding multiple physical paths using #push" do
+ @root.add "app"
+ @root["app"].push "app2"
+ assert_equal ["/foo/bar/app", "/foo/bar/app2"], @root["app"].to_a
+ end
+
+ test "adding multiple physical paths using <<" do
+ @root.add "app"
+ @root["app"] << "app2"
+ assert_equal ["/foo/bar/app", "/foo/bar/app2"], @root["app"].to_a
+ end
+
+ test "adding multiple physical paths using concat" do
+ @root.add "app"
+ @root["app"].concat ["app2", "/app3"]
+ assert_equal ["/foo/bar/app", "/foo/bar/app2", "/app3"], @root["app"].to_a
+ end
+
+ test "adding multiple physical paths using #unshift" do
+ @root.add "app"
+ @root["app"].unshift "app2"
+ assert_equal ["/foo/bar/app2", "/foo/bar/app"], @root["app"].to_a
+ end
+
+ test "it is possible to add a path that should be autoloaded only once" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app"
+ @root["app"].autoload_once!
+ assert @root["app"].autoload_once?
+ assert @root.autoload_once.include?(@root["app"].expanded.first)
+ end
+ end
+
+ test "it is possible to remove a path that should be autoloaded only once" do
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ assert @root["app"].autoload_once?
+
+ @root["app"].skip_autoload_once!
+ assert !@root["app"].autoload_once?
+ assert !@root.autoload_once.include?(@root["app"].expanded.first)
+ end
+
+ test "it is possible to add a path without assignment and specify it should be loaded only once" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", autoload_once: true
+ assert @root["app"].autoload_once?
+ assert @root.autoload_once.include?("/app")
+ end
+ end
+
+ test "it is possible to add multiple paths without assignment and specify it should be loaded only once" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: ["/app", "/app2"], autoload_once: true
+ assert @root["app"].autoload_once?
+ assert @root.autoload_once.include?("/app")
+ assert @root.autoload_once.include?("/app2")
+ end
+ end
+
+ test "making a path autoload_once more than once only includes it once in @root.load_once" do
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ @root["app"].autoload_once!
+ assert_equal 1, @root.autoload_once.select {|p| p == @root["app"].expanded.first }.size
+ end
+ end
+
+ test "paths added to a load_once path should be added to the autoload_once collection" do
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ @root["app"] << "/app2"
+ assert_equal 2, @root.autoload_once.size
+ end
+ end
+
+ test "it is possible to mark a path as eager loaded" do
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?(@root["app"].to_a.first)
+ end
+ end
+
+ test "it is possible to skip a path from eager loading" do
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ assert @root["app"].eager_load?
+
+ @root["app"].skip_eager_load!
+ assert !@root["app"].eager_load?
+ assert !@root.eager_load.include?(@root["app"].to_a.first)
+ end
+
+ test "it is possible to add a path without assignment and mark it as eager" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", eager_load: true
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?("/app")
+ end
+ end
+
+ test "it is possible to add multiple paths without assignment and mark them as eager" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: ["/app", "/app2"], eager_load: true
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?("/app")
+ assert @root.eager_load.include?("/app2")
+ end
+ end
+
+ test "it is possible to create a path without assignment and mark it both as eager and load once" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", eager_load: true, autoload_once: true
+ assert @root["app"].eager_load?
+ assert @root["app"].autoload_once?
+ assert @root.eager_load.include?("/app")
+ assert @root.autoload_once.include?("/app")
+ end
+ end
+
+ test "making a path eager more than once only includes it once in @root.eager_paths" do
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ @root["app"].eager_load!
+ assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size
+ end
+ end
+
+ test "paths added to an eager_load path should be added to the eager_load collection" do
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ @root["app"] << "/app2"
+ assert_equal 2, @root.eager_load.size
+ end
+ end
+
+ test "it should be possible to add a path's default glob" do
+ @root["app"] = "/app"
+ @root["app"].glob = "*.rb"
+ assert_equal "*.rb", @root["app"].glob
+ end
+
+ test "it should be possible to get extensions by glob" do
+ @root["app"] = "/app"
+ @root["app"].glob = "*.{rb,yml}"
+ assert_equal ["rb", "yml"], @root["app"].extensions
+ end
+
+ test "it should be possible to override a path's default glob without assignment" do
+ @root.add "app", with: "/app", glob: "*.rb"
+ assert_equal "*.rb", @root["app"].glob
+ end
+
+ test "it should be possible to replace a path and persist the original paths glob" do
+ @root.add "app", glob: "*.rb"
+ @root["app"] = "app2"
+ assert_equal ["/foo/bar/app2"], @root["app"].to_a
+ assert_equal "*.rb", @root["app"].glob
+ end
+
+ test "a path can be added to the load path" do
+ File.stub(:exist?, true) do
+ @root["app"] = "app"
+ @root["app"].load_path!
+ @root["app/models"] = "app/models"
+ assert_equal ["/foo/bar/app"], @root.load_paths
+ end
+ end
+
+ test "a path can be added to the load path on creation" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", load_path: true
+ assert @root["app"].load_path?
+ assert_equal ["/app"], @root.load_paths
+ end
+ end
+
+ test "a path can be marked as autoload path" do
+ File.stub(:exist?, true) do
+ @root["app"] = "app"
+ @root["app"].autoload!
+ @root["app/models"] = "app/models"
+ assert_equal ["/foo/bar/app"], @root.autoload_paths
+ end
+ end
+
+ test "a path can be marked as autoload on creation" do
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", autoload: true
+ assert @root["app"].autoload?
+ assert_equal ["/app"], @root.autoload_paths
+ end
+ end
+end
diff --git a/railties/test/rack_logger_test.rb b/railties/test/rack_logger_test.rb
new file mode 100644
index 0000000000..fcc79b57fb
--- /dev/null
+++ b/railties/test/rack_logger_test.rb
@@ -0,0 +1,75 @@
+require 'abstract_unit'
+require 'active_support/testing/autorun'
+require 'active_support/test_case'
+require 'rails/rack/logger'
+require 'logger'
+
+module Rails
+ module Rack
+ class LoggerTest < ActiveSupport::TestCase
+ class TestLogger < Rails::Rack::Logger
+ NULL = ::Logger.new File::NULL
+
+ attr_reader :logger
+
+ def initialize(logger = NULL, taggers = nil, &block)
+ super(->(_) { block.call; [200, {}, []] }, taggers)
+ @logger = logger
+ end
+
+ def development?; false; end
+ end
+
+ class Subscriber < Struct.new(:starts, :finishes)
+ def initialize(starts = [], finishes = [])
+ super
+ end
+
+ def start(name, id, payload)
+ starts << [name, id, payload]
+ end
+
+ def finish(name, id, payload)
+ finishes << [name, id, payload]
+ end
+ end
+
+ attr_reader :subscriber, :notifier
+
+ def setup
+ @subscriber = Subscriber.new
+ @notifier = ActiveSupport::Notifications.notifier
+ @subscription = notifier.subscribe 'request.action_dispatch', subscriber
+ end
+
+ def teardown
+ notifier.unsubscribe @subscription
+ end
+
+ def test_notification
+ logger = TestLogger.new { }
+
+ assert_difference('subscriber.starts.length') do
+ assert_difference('subscriber.finishes.length') do
+ logger.call('REQUEST_METHOD' => 'GET').last.close
+ end
+ end
+ end
+
+ def test_notification_on_raise
+ logger = TestLogger.new do
+ # using an exception class that is not a StandardError subclass on purpose
+ raise NotImplementedError
+ end
+
+ assert_difference('subscriber.starts.length') do
+ assert_difference('subscriber.finishes.length') do
+ assert_raises(NotImplementedError) do
+ logger.call 'REQUEST_METHOD' => 'GET'
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb
new file mode 100644
index 0000000000..c51503c2b7
--- /dev/null
+++ b/railties/test/rails_info_controller_test.rb
@@ -0,0 +1,81 @@
+require 'abstract_unit'
+
+module ActionController
+ class Base
+ include ActionController::Testing
+ end
+end
+
+class InfoControllerTest < ActionController::TestCase
+ tests Rails::InfoController
+
+ def setup
+ Rails.application.routes.draw do
+ get '/rails/info/properties' => "rails/info#properties"
+ get '/rails/info/routes' => "rails/info#routes"
+ end
+ @routes = Rails.application.routes
+
+ Rails::InfoController.include(@routes.url_helpers)
+
+ @request.env["REMOTE_ADDR"] = "127.0.0.1"
+ end
+
+ test "info controller does not allow remote requests" do
+ @request.env["REMOTE_ADDR"] = "example.org"
+ get :properties
+ assert_response :forbidden
+ end
+
+ test "info controller renders an error message when request was forbidden" do
+ @request.env["REMOTE_ADDR"] = "example.org"
+ get :properties
+ assert_select 'p'
+ end
+
+ test "info controller allows requests when all requests are considered local" do
+ get :properties
+ assert_response :success
+ end
+
+ test "info controller allows local requests" do
+ get :properties
+ assert_response :success
+ end
+
+ test "info controller renders a table with properties" do
+ get :properties
+ assert_select 'table'
+ end
+
+ test "info controller renders with routes" do
+ get :routes
+ assert_response :success
+ end
+
+ test "info controller returns exact matches" do
+ exact_count = -> { JSON(response.body)['exact'].size }
+
+ get :routes, params: { path: 'rails/info/route' }
+ assert exact_count.call == 0, 'should not match incomplete routes'
+
+ get :routes, params: { path: 'rails/info/routes' }
+ assert exact_count.call == 1, 'should match complete routes'
+
+ get :routes, params: { path: 'rails/info/routes.html' }
+ assert exact_count.call == 1, 'should match complete routes with optional parts'
+ end
+
+ test "info controller returns fuzzy matches" do
+ fuzzy_count = -> { JSON(response.body)['fuzzy'].size }
+
+ get :routes, params: { path: 'rails/info' }
+ assert fuzzy_count.call == 2, 'should match incomplete routes'
+
+ get :routes, params: { path: 'rails/info/routes' }
+ assert fuzzy_count.call == 1, 'should match complete routes'
+
+ get :routes, params: { path: 'rails/info/routes.html' }
+ assert fuzzy_count.call == 0, 'should match optional parts of route literally'
+ end
+end
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
new file mode 100644
index 0000000000..92e4af25b5
--- /dev/null
+++ b/railties/test/rails_info_test.rb
@@ -0,0 +1,71 @@
+require 'abstract_unit'
+
+unless defined?(Rails) && defined?(Rails::Info)
+ module Rails
+ class Info; end
+ end
+end
+
+require "active_support/core_ext/kernel/reporting"
+
+class InfoTest < ActiveSupport::TestCase
+ def setup
+ Rails.send :remove_const, :Info
+ silence_warnings { load 'rails/info.rb' }
+ end
+
+ def test_property_with_block_swallows_exceptions_and_ignores_property
+ assert_nothing_raised do
+ Rails::Info.module_eval do
+ property('Bogus') {raise}
+ end
+ end
+ assert !property_defined?('Bogus')
+ end
+
+ def test_property_with_string
+ Rails::Info.module_eval do
+ property 'Hello', 'World'
+ end
+ assert_property 'Hello', 'World'
+ end
+
+ def test_property_with_block
+ Rails::Info.module_eval do
+ property('Goodbye') {'World'}
+ end
+ assert_property 'Goodbye', 'World'
+ end
+
+ def test_rails_version
+ assert_property 'Rails version',
+ File.read(File.realpath('../../../RAILS_VERSION', __FILE__)).chomp
+ end
+
+ def test_html_includes_middleware
+ Rails::Info.module_eval do
+ property 'Middleware', ['Rack::Lock', 'Rack::Static']
+ end
+
+ html = Rails::Info.to_html
+ assert html.include?('<tr><td class="name">Middleware</td>')
+ properties.value_for('Middleware').each do |value|
+ assert html.include?("<li>#{CGI.escapeHTML(value)}</li>")
+ end
+ end
+
+ protected
+ def properties
+ Rails::Info.properties
+ end
+
+ def property_defined?(property_name)
+ properties.names.include? property_name
+ end
+
+ def assert_property(property_name, value)
+ raise "Property #{property_name.inspect} not defined" unless
+ property_defined? property_name
+ assert_equal value, properties.value_for(property_name)
+ end
+end
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
new file mode 100644
index 0000000000..24386de82a
--- /dev/null
+++ b/railties/test/railties/engine_test.rb
@@ -0,0 +1,1361 @@
+require "isolation/abstract_unit"
+require "stringio"
+require "rack/test"
+
+module RailtiesTest
+ class EngineTest < ActiveSupport::TestCase
+
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+
+ @plugin = engine "bukkits" do |plugin|
+ plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ railtie_name "bukkits"
+ end
+ end
+ RUBY
+ plugin.write "lib/another.rb", "class Another; end"
+ end
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def boot_rails
+ super
+ require "#{app_path}/config/environment"
+ end
+
+ test "serving sprocket's assets" do
+ @plugin.write "app/assets/javascripts/engine.js.erb", "<%= :alert %>();"
+ add_to_env_config "development", "config.assets.digest = false"
+
+ boot_rails
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/assets/engine.js"
+ assert_match "alert()", last_response.body
+ end
+
+ test "rake environment can be called in the engine" do
+ boot_rails
+
+ @plugin.write "Rakefile", <<-RUBY
+ APP_RAKEFILE = '#{app_path}/Rakefile'
+ load 'rails/tasks/engine.rake'
+ task :foo => :environment do
+ puts "Task ran"
+ end
+ RUBY
+
+ Dir.chdir(@plugin.path) do
+ output = `bundle exec rake foo`
+ assert_match "Task ran", output
+ end
+ end
+
+ test "copying migrations" do
+ @plugin.write "db/migrate/1_create_users.rb", <<-RUBY
+ class CreateUsers < ActiveRecord::Migration
+ end
+ RUBY
+
+ @plugin.write "db/migrate/2_add_last_name_to_users.rb", <<-RUBY
+ class AddLastNameToUsers < ActiveRecord::Migration
+ end
+ RUBY
+
+ @plugin.write "db/migrate/3_create_sessions.rb", <<-RUBY
+ class CreateSessions < ActiveRecord::Migration
+ end
+ RUBY
+
+ app_file "db/migrate/1_create_sessions.rb", <<-RUBY
+ class CreateSessions < ActiveRecord::Migration
+ def up
+ end
+ end
+ RUBY
+
+ add_to_config "ActiveRecord::Base.timestamped_migrations = false"
+
+ boot_rails
+
+ Dir.chdir(app_path) do
+ output = `bundle exec rake bukkits:install:migrations`
+
+ assert File.exist?("#{app_path}/db/migrate/2_create_users.bukkits.rb")
+ assert File.exist?("#{app_path}/db/migrate/3_add_last_name_to_users.bukkits.rb")
+ assert_match(/Copied migration 2_create_users.bukkits.rb from bukkits/, output)
+ assert_match(/Copied migration 3_add_last_name_to_users.bukkits.rb from bukkits/, output)
+ assert_match(/NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/, output)
+ assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length
+
+ output = `bundle exec rake railties:install:migrations`.split("\n")
+
+ assert_no_match(/2_create_users/, output.join("\n"))
+
+ bukkits_migration_order = output.index(output.detect{|o| /NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/ =~ o })
+ assert_not_nil bukkits_migration_order, "Expected migration to be skipped"
+
+ migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length
+ `bundle exec rake railties:install:migrations`
+
+ assert_equal migrations_count, Dir["#{app_path}/db/migrate/*.rb"].length
+ end
+ end
+
+ test 'respects the order of railties when installing migrations' do
+ @blog = engine "blog" do |plugin|
+ plugin.write "lib/blog.rb", <<-RUBY
+ module Blog
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+ end
+
+ @plugin.write "db/migrate/1_create_users.rb", <<-RUBY
+ class CreateUsers < ActiveRecord::Migration
+ end
+ RUBY
+
+ @blog.write "db/migrate/2_create_blogs.rb", <<-RUBY
+ class CreateBlogs < ActiveRecord::Migration
+ end
+ RUBY
+
+ add_to_config("config.railties_order = [Bukkits::Engine, Blog::Engine, :all, :main_app]")
+
+ boot_rails
+
+ Dir.chdir(app_path) do
+ output = `bundle exec rake railties:install:migrations`.split("\n")
+
+ assert_match(/Copied migration \d+_create_users.bukkits.rb from bukkits/, output.first)
+ assert_match(/Copied migration \d+_create_blogs.blog_engine.rb from blog_engine/, output.last)
+ end
+ end
+
+ test "dont reverse default railties order" do
+ @api = engine "api" do |plugin|
+ plugin.write "lib/api.rb", <<-RUBY
+ module Api
+ class Engine < ::Rails::Engine; end
+ end
+ RUBY
+ end
+
+ # added last but here is loaded before api engine
+ @core = engine "core" do |plugin|
+ plugin.write "lib/core.rb", <<-RUBY
+ module Core
+ class Engine < ::Rails::Engine; end
+ end
+ RUBY
+ end
+
+ @core.write "db/migrate/1_create_users.rb", <<-RUBY
+ class CreateUsers < ActiveRecord::Migration; end
+ RUBY
+
+ @api.write "db/migrate/2_create_keys.rb", <<-RUBY
+ class CreateKeys < ActiveRecord::Migration; end
+ RUBY
+
+ boot_rails
+
+ Dir.chdir(app_path) do
+ output = `bundle exec rake railties:install:migrations`.split("\n")
+
+ assert_match(/Copied migration \d+_create_users.core_engine.rb from core_engine/, output.first)
+ assert_match(/Copied migration \d+_create_keys.api_engine.rb from api_engine/, output.last)
+ end
+ end
+
+ test "mountable engine should copy migrations within engine_path" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ @plugin.write "db/migrate/0_add_first_name_to_users.rb", <<-RUBY
+ class AddFirstNameToUsers < ActiveRecord::Migration
+ end
+ RUBY
+
+ @plugin.write "Rakefile", <<-RUBY
+ APP_RAKEFILE = '#{app_path}/Rakefile'
+ load 'rails/tasks/engine.rake'
+ RUBY
+
+ add_to_config "ActiveRecord::Base.timestamped_migrations = false"
+
+ boot_rails
+
+ Dir.chdir(@plugin.path) do
+ output = `bundle exec rake app:bukkits:install:migrations`
+ assert File.exist?("#{app_path}/db/migrate/0_add_first_name_to_users.bukkits.rb")
+ assert_match(/Copied migration 0_add_first_name_to_users.bukkits.rb from bukkits/, output)
+ assert_equal 1, Dir["#{app_path}/db/migrate/*.rb"].length
+ end
+ end
+
+ test "no rake task without migrations" do
+ boot_rails
+ require 'rake'
+ require 'rdoc/task'
+ require 'rake/testtask'
+ Rails.application.load_tasks
+ assert !Rake::Task.task_defined?('bukkits:install:migrations')
+ end
+
+ test "puts its lib directory on load path" do
+ boot_rails
+ require "another"
+ assert_equal "Another", Another.name
+ end
+
+ test "puts its models directory on autoload path" do
+ @plugin.write "app/models/my_bukkit.rb", "class MyBukkit ; end"
+ boot_rails
+ assert_nothing_raised { MyBukkit }
+ end
+
+ test "puts its controllers directory on autoload path" do
+ @plugin.write "app/controllers/bukkit_controller.rb", "class BukkitController ; end"
+ boot_rails
+ assert_nothing_raised { BukkitController }
+ end
+
+ test "adds its views to view paths" do
+ @plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
+ class BukkitController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/bukkit/index.html.erb", "Hello bukkits"
+
+ boot_rails
+
+ require "action_controller"
+ require "rack/mock"
+ response = BukkitController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal "Hello bukkits\n", response[2].body
+ end
+
+ test "adds its views to view paths with lower priority than app ones" do
+ @plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
+ class BukkitController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/bukkit/index.html.erb", "Hello bukkits"
+ app_file "app/views/bukkit/index.html.erb", "Hi bukkits"
+
+ boot_rails
+
+ require "action_controller"
+ require "rack/mock"
+ response = BukkitController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal "Hi bukkits\n", response[2].body
+ end
+
+ test "adds helpers to controller views" do
+ @plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
+ class BukkitController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ @plugin.write "app/helpers/bukkit_helper.rb", <<-RUBY
+ module BukkitHelper
+ def bukkits
+ "bukkits"
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/bukkit/index.html.erb", "Hello <%= bukkits %>"
+
+ boot_rails
+
+ require "rack/mock"
+ response = BukkitController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal "Hello bukkits\n", response[2].body
+ end
+
+ test "autoload any path under app" do
+ @plugin.write "app/anything/foo.rb", <<-RUBY
+ module Foo; end
+ RUBY
+ boot_rails
+ assert Foo
+ end
+
+ test "routes are added to router" do
+ @plugin.write "config/routes.rb", <<-RUBY
+ class Sprokkit
+ def self.call(env)
+ [200, {'Content-Type' => 'text/html'}, ["I am a Sprokkit"]]
+ end
+ end
+
+ Rails.application.routes.draw do
+ get "/sprokkit", :to => Sprokkit
+ end
+ RUBY
+
+ boot_rails
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/sprokkit"
+ assert_equal "I am a Sprokkit", last_response.body
+ end
+
+ test "routes in engines have lower priority than application ones" do
+ controller "foo", <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ render :text => "foo"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', :to => 'foo#index'
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bar_controller.rb", <<-RUBY
+ class BarController < ActionController::Base
+ def index
+ render :text => "bar"
+ end
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'bar#index'
+ get 'bar', to: 'bar#index'
+ end
+ RUBY
+
+ boot_rails
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+
+ get '/bar'
+ assert_equal 'bar', last_response.body
+ end
+
+ test "rake tasks lib tasks are loaded" do
+ $executed = false
+ @plugin.write "lib/tasks/foo.rake", <<-RUBY
+ task :foo do
+ $executed = true
+ end
+ RUBY
+
+ boot_rails
+ require 'rake'
+ require 'rdoc/task'
+ require 'rake/testtask'
+ Rails.application.load_tasks
+ Rake::Task[:foo].invoke
+ assert $executed
+ end
+
+ test "i18n files have lower priority than application ones" do
+ add_to_config <<-RUBY
+ config.i18n.load_path << "#{app_path}/app/locales/en.yml"
+ RUBY
+
+ app_file 'app/locales/en.yml', <<-YAML
+en:
+ bar: "1"
+YAML
+
+ app_file 'config/locales/en.yml', <<-YAML
+en:
+ foo: "2"
+ bar: "2"
+YAML
+
+ @plugin.write 'config/locales/en.yml', <<-YAML
+en:
+ foo: "3"
+YAML
+
+ boot_rails
+
+ expected_locales = %W(
+ #{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/locale/en.yml
+ #{RAILS_FRAMEWORK_ROOT}/activemodel/lib/active_model/locale/en.yml
+ #{RAILS_FRAMEWORK_ROOT}/activerecord/lib/active_record/locale/en.yml
+ #{RAILS_FRAMEWORK_ROOT}/actionview/lib/action_view/locale/en.yml
+ #{@plugin.path}/config/locales/en.yml
+ #{app_path}/config/locales/en.yml
+ #{app_path}/app/locales/en.yml
+ ).map { |path| File.expand_path(path) }
+
+ actual_locales = I18n.load_path.map { |path|
+ File.expand_path(path)
+ } & expected_locales # remove locales external to Rails
+
+ assert_equal expected_locales, actual_locales
+
+ assert_equal "2", I18n.t(:foo)
+ assert_equal "1", I18n.t(:bar)
+ end
+
+ test "namespaced controllers with namespaced routes" do
+ @plugin.write "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ namespace :admin do
+ namespace :foo do
+ get "bar", to: "bar#index"
+ end
+ end
+ end
+ RUBY
+
+ @plugin.write "app/controllers/admin/foo/bar_controller.rb", <<-RUBY
+ class Admin::Foo::BarController < ApplicationController
+ def index
+ render text: "Rendered from namespace"
+ end
+ end
+ RUBY
+
+ boot_rails
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/admin/foo/bar"
+ assert_equal 200, last_response.status
+ assert_equal "Rendered from namespace", last_response.body
+ end
+
+ test "initializers" do
+ $plugin_initializer = false
+ @plugin.write "config/initializers/foo.rb", <<-RUBY
+ $plugin_initializer = true
+ RUBY
+
+ boot_rails
+ assert $plugin_initializer
+ end
+
+ test "middleware referenced in configuration" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ @app.call(env)
+ end
+ end
+ RUBY
+
+ add_to_config "config.middleware.use \"Bukkits\""
+ boot_rails
+ end
+
+ test "initializers are executed after application configuration initializers" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ initializer "dummy_initializer" do
+ end
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ initializers = Rails.application.initializers.tsort
+ dummy_index = initializers.index { |i| i.name == "dummy_initializer" }
+ config_index = initializers.rindex { |i| i.name == :load_config_initializers }
+ stack_index = initializers.index { |i| i.name == :build_middleware_stack }
+
+ assert config_index < dummy_index
+ assert dummy_index < stack_index
+ end
+
+ class Upcaser
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ response = @app.call(env)
+ response[2].each(&:upcase!)
+ response
+ end
+ end
+
+ test "engine is a rack app and can have its own middleware stack" do
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ endpoint lambda { |env| [200, {'Content-Type' => 'text/html'}, ['Hello World']] }
+ config.middleware.use ::RailtiesTest::EngineTest::Upcaser
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount(Bukkits::Engine => "/bukkits")
+ end
+ RUBY
+
+ boot_rails
+
+ get("/bukkits")
+ assert_equal "HELLO WORLD", last_response.body
+ end
+
+ test "pass the value of the segment" do
+ controller "foo", <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ render text: params[:username]
+ end
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ root to: "foo#index"
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount(Bukkits::Engine => "/:username")
+ end
+ RUBY
+
+ boot_rails
+
+ get("/arunagw")
+ assert_equal "arunagw", last_response.body
+
+ end
+
+ test "it provides routes as default endpoint" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ get "/foo" => lambda { |env| [200, {'Content-Type' => 'text/html'}, ['foo']] }
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount(Bukkits::Engine => "/bukkits")
+ end
+ RUBY
+
+ boot_rails
+
+ get("/bukkits/foo")
+ assert_equal "foo", last_response.body
+ end
+
+ test "it loads its environments file" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ config.paths["config/environments"].push "config/environments/additional.rb"
+ end
+ end
+ RUBY
+
+ @plugin.write "config/environments/development.rb", <<-RUBY
+ Bukkits::Engine.configure do
+ config.environment_loaded = true
+ end
+ RUBY
+
+ @plugin.write "config/environments/additional.rb", <<-RUBY
+ Bukkits::Engine.configure do
+ config.additional_environment_loaded = true
+ end
+ RUBY
+
+ boot_rails
+
+ assert Bukkits::Engine.config.environment_loaded
+ assert Bukkits::Engine.config.additional_environment_loaded
+ end
+
+ test "it passes router in env" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ endpoint lambda { |env| [200, {'Content-Type' => 'text/html'}, ['hello']] }
+ end
+ end
+ RUBY
+
+ require "rack/file"
+ boot_rails
+
+ env = Rack::MockRequest.env_for("/")
+ Bukkits::Engine.call(env)
+ assert_equal Bukkits::Engine.routes, env['action_dispatch.routes']
+
+ env = Rack::MockRequest.env_for("/")
+ Rails.application.call(env)
+ assert_equal Rails.application.routes, env['action_dispatch.routes']
+ end
+
+ test "isolated engine should include only its own routes and helpers" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ @plugin.write "app/models/bukkits/post.rb", <<-RUBY
+ module Bukkits
+ class Post
+ include ActiveModel::Model
+
+ def to_param
+ "1"
+ end
+
+ def persisted?
+ true
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/bar" => "bar#index", as: "bar"
+ mount Bukkits::Engine => "/bukkits", as: "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ get "/foo" => "foo#index", as: "foo"
+ get "/foo/show" => "foo#show"
+ get "/from_app" => "foo#from_app"
+ get "/routes_helpers_in_view" => "foo#routes_helpers_in_view"
+ get "/polymorphic_path_without_namespace" => "foo#polymorphic_path_without_namespace"
+ resources :posts
+ end
+ RUBY
+
+ app_file "app/helpers/some_helper.rb", <<-RUBY
+ module SomeHelper
+ def something
+ "Something... Something... Something..."
+ end
+ end
+ RUBY
+
+ @plugin.write "app/helpers/engine_helper.rb", <<-RUBY
+ module EngineHelper
+ def help_the_engine
+ "Helped."
+ end
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/foo_controller.rb", <<-RUBY
+ class Bukkits::FooController < ActionController::Base
+ def index
+ render inline: "<%= help_the_engine %>"
+ end
+
+ def show
+ render text: foo_path
+ end
+
+ def from_app
+ render inline: "<%= (self.respond_to?(:bar_path) || self.respond_to?(:something)) %>"
+ end
+
+ def routes_helpers_in_view
+ render inline: "<%= foo_path %>, <%= main_app.bar_path %>"
+ end
+
+ def polymorphic_path_without_namespace
+ render text: polymorphic_path(Post.new)
+ end
+ end
+ RUBY
+
+ @plugin.write "app/mailers/bukkits/my_mailer.rb", <<-RUBY
+ module Bukkits
+ class MyMailer < ActionMailer::Base
+ end
+ end
+ RUBY
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ boot_rails
+
+ assert_equal "bukkits_", Bukkits.table_name_prefix
+ assert_equal "bukkits", Bukkits::Engine.engine_name
+ assert_equal Bukkits.railtie_namespace, Bukkits::Engine
+ assert ::Bukkits::MyMailer.method_defined?(:foo_url)
+ assert !::Bukkits::MyMailer.method_defined?(:bar_url)
+
+ get("/bukkits/from_app")
+ assert_equal "false", last_response.body
+
+ get("/bukkits/foo/show")
+ assert_equal "/bukkits/foo", last_response.body
+
+ get("/bukkits/foo")
+ assert_equal "Helped.", last_response.body
+
+ get("/bukkits/routes_helpers_in_view")
+ assert_equal "/bukkits/foo, /bar", last_response.body
+
+ get("/bukkits/polymorphic_path_without_namespace")
+ assert_equal "/bukkits/posts/1", last_response.body
+ end
+
+ test "isolated engine should avoid namespace in names if that's possible" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ @plugin.write "app/models/bukkits/post.rb", <<-RUBY
+ module Bukkits
+ class Post
+ include ActiveModel::Model
+ attr_accessor :title
+
+ def to_param
+ "1"
+ end
+
+ def persisted?
+ false
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount Bukkits::Engine => "/bukkits", as: "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ resources :posts
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/posts_controller.rb", <<-RUBY
+ class Bukkits::PostsController < ActionController::Base
+ def new
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/bukkits/posts/new.html.erb", <<-ERB
+ <%= form_for(Bukkits::Post.new) do |f| %>
+ <%= f.text_field :title %>
+ <% end %>
+ ERB
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ boot_rails
+
+ get("/bukkits/posts/new")
+ assert_match(/name="post\[title\]"/, last_response.body)
+ end
+
+ test "isolated engine should set correct route module prefix for nested namespace" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ module Awesome
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits::Awesome
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount Bukkits::Awesome::Engine => "/bukkits", :as => "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Awesome::Engine.routes.draw do
+ get "/foo" => "foo#index"
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/awesome/foo_controller.rb", <<-RUBY
+ class Bukkits::Awesome::FooController < ActionController::Base
+ def index
+ render :text => "ok"
+ end
+ end
+ RUBY
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ boot_rails
+
+ get("/bukkits/foo")
+ assert_equal "ok", last_response.body
+ end
+
+ test "loading seed data" do
+ @plugin.write "db/seeds.rb", <<-RUBY
+ Bukkits::Engine.config.bukkits_seeds_loaded = true
+ RUBY
+
+ app_file "db/seeds.rb", <<-RUBY
+ Rails.application.config.app_seeds_loaded = true
+ RUBY
+
+ boot_rails
+
+ Rails.application.load_seed
+ assert Rails.application.config.app_seeds_loaded
+ assert_raise(NoMethodError) { Bukkits::Engine.config.bukkits_seeds_loaded }
+
+ Bukkits::Engine.load_seed
+ assert Bukkits::Engine.config.bukkits_seeds_loaded
+ end
+
+ test "skips nonexistent seed data" do
+ FileUtils.rm "#{app_path}/db/seeds.rb"
+ boot_rails
+ assert_nil Rails.application.load_seed
+ end
+
+ test "using namespace more than once on one module should not overwrite railtie_namespace method" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module AppTemplate
+ class Engine < ::Rails::Engine
+ isolate_namespace(AppTemplate)
+ end
+ end
+ RUBY
+
+ add_to_config "isolate_namespace AppTemplate"
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do end
+ RUBY
+
+ boot_rails
+
+ assert_equal AppTemplate.railtie_namespace, AppTemplate::Engine
+ end
+
+ test "properly reload routes" do
+ # when routes are inside application class definition
+ # they should not be reloaded when engine's routes
+ # file has changed
+ add_to_config <<-RUBY
+ routes do
+ mount lambda{|env| [200, {}, ["foo"]]} => "/foo"
+ mount Bukkits::Engine => "/bukkits"
+ end
+ RUBY
+
+ FileUtils.rm(File.join(app_path, "config/routes.rb"))
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ mount lambda{|env| [200, {}, ["bar"]]} => "/bar"
+ end
+ RUBY
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace(Bukkits)
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ get("/foo")
+ assert_equal "foo", last_response.body
+
+ get("/bukkits/bar")
+ assert_equal "bar", last_response.body
+ end
+
+ test "setting generators for engine and overriding app generator's" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ config.generators do |g|
+ g.orm :data_mapper
+ g.template_engine :haml
+ g.test_framework :rspec
+ end
+
+ config.app_generators do |g|
+ g.orm :mongoid
+ g.template_engine :liquid
+ g.test_framework :shoulda
+ end
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.generators do |g|
+ g.test_framework :test_unit
+ end
+ RUBY
+
+ boot_rails
+
+ app_generators = Rails.application.config.generators.options[:rails]
+ assert_equal :mongoid , app_generators[:orm]
+ assert_equal :liquid , app_generators[:template_engine]
+ assert_equal :test_unit, app_generators[:test_framework]
+
+ generators = Bukkits::Engine.config.generators.options[:rails]
+ assert_equal :data_mapper, generators[:orm]
+ assert_equal :haml , generators[:template_engine]
+ assert_equal :rspec , generators[:test_framework]
+ end
+
+ test "engine should get default generators with ability to overwrite them" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ config.generators.test_framework :rspec
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ generators = Bukkits::Engine.config.generators.options[:rails]
+ assert_equal :active_record, generators[:orm]
+ assert_equal :rspec , generators[:test_framework]
+
+ app_generators = Rails.application.config.generators.options[:rails]
+ assert_equal :test_unit , app_generators[:test_framework]
+ end
+
+ test "do not create table_name_prefix method if it already exists" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ def self.table_name_prefix
+ "foo"
+ end
+
+ class Engine < ::Rails::Engine
+ isolate_namespace(Bukkits)
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ assert_equal "foo", Bukkits.table_name_prefix
+ end
+
+ test "fetching engine by path" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ assert_equal Bukkits::Engine.instance, Rails::Engine.find(@plugin.path)
+
+ # check expanding paths
+ engine_dir = @plugin.path.chomp("/").split("/").last
+ engine_path = File.join(@plugin.path, '..', engine_dir)
+ assert_equal Bukkits::Engine.instance, Rails::Engine.find(engine_path)
+ end
+
+ test "gather isolated engine's helpers in Engine#helpers" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/some_helper.rb", <<-RUBY
+ module SomeHelper
+ def foo
+ 'foo'
+ end
+ end
+ RUBY
+
+ @plugin.write "app/helpers/bukkits/engine_helper.rb", <<-RUBY
+ module Bukkits
+ module EngineHelper
+ def bar
+ 'bar'
+ end
+ end
+ end
+ RUBY
+
+ @plugin.write "app/helpers/engine_helper.rb", <<-RUBY
+ module EngineHelper
+ def baz
+ 'baz'
+ end
+ end
+ RUBY
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ boot_rails
+
+ assert_equal [:bar, :baz], Bukkits::Engine.helpers.public_instance_methods.sort
+ end
+
+ test "setting priority for engines with config.railties_order" do
+ @blog = engine "blog" do |plugin|
+ plugin.write "lib/blog.rb", <<-RUBY
+ module Blog
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+ end
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ controller "main", <<-RUBY
+ class MainController < ActionController::Base
+ def foo
+ render inline: '<%= render :partial => "shared/foo" %>'
+ end
+
+ def bar
+ render inline: '<%= render :partial => "shared/bar" %>'
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/foo" => "main#foo"
+ get "/bar" => "main#bar"
+ end
+ RUBY
+
+ @plugin.write "app/views/shared/_foo.html.erb", <<-RUBY
+ Bukkit's foo partial
+ RUBY
+
+ app_file "app/views/shared/_foo.html.erb", <<-RUBY
+ App's foo partial
+ RUBY
+
+ @blog.write "app/views/shared/_bar.html.erb", <<-RUBY
+ Blog's bar partial
+ RUBY
+
+ app_file "app/views/shared/_bar.html.erb", <<-RUBY
+ App's bar partial
+ RUBY
+
+ @plugin.write "app/assets/javascripts/foo.js", <<-RUBY
+ // Bukkit's foo js
+ RUBY
+
+ app_file "app/assets/javascripts/foo.js", <<-RUBY
+ // App's foo js
+ RUBY
+
+ @blog.write "app/assets/javascripts/bar.js", <<-RUBY
+ // Blog's bar js
+ RUBY
+
+ app_file "app/assets/javascripts/bar.js", <<-RUBY
+ // App's bar js
+ RUBY
+
+ add_to_config("config.railties_order = [:all, :main_app, Blog::Engine]")
+ add_to_env_config "development", "config.assets.digest = false"
+
+ boot_rails
+
+ get("/foo")
+ assert_equal "Bukkit's foo partial", last_response.body.strip
+
+ get("/bar")
+ assert_equal "App's bar partial", last_response.body.strip
+
+ get("/assets/foo.js")
+ assert_equal "// Bukkit's foo js", last_response.body.strip
+
+ get("/assets/bar.js")
+ assert_equal "// App's bar js", last_response.body.strip
+
+ # ensure that railties are not added twice
+ railties = Rails.application.send(:ordered_railties).map(&:class)
+ assert_equal railties, railties.uniq
+ end
+
+ test "railties_order adds :all with lowest priority if not given" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ controller "main", <<-RUBY
+ class MainController < ActionController::Base
+ def foo
+ render inline: '<%= render :partial => "shared/foo" %>'
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/foo" => "main#foo"
+ end
+ RUBY
+
+ @plugin.write "app/views/shared/_foo.html.erb", <<-RUBY
+ Bukkit's foo partial
+ RUBY
+
+ app_file "app/views/shared/_foo.html.erb", <<-RUBY
+ App's foo partial
+ RUBY
+
+ add_to_config("config.railties_order = [Bukkits::Engine]")
+
+ boot_rails
+
+ get("/foo")
+ assert_equal "Bukkit's foo partial", last_response.body.strip
+ end
+
+ test "engine can be properly mounted at root" do
+ add_to_config("config.action_dispatch.show_exceptions = false")
+ add_to_config("config.public_file_server.enabled = false")
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace ::Bukkits
+ end
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ root "foo#index"
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/foo_controller.rb", <<-RUBY
+ module Bukkits
+ class FooController < ActionController::Base
+ def index
+ text = <<-TEXT
+ script_name: \#{request.script_name}
+ fullpath: \#{request.fullpath}
+ path: \#{request.path}
+ TEXT
+ render text: text
+ end
+ end
+ end
+ RUBY
+
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount Bukkits::Engine => "/"
+ end
+ RUBY
+
+ boot_rails
+
+ expected = <<-TEXT
+ script_name:
+ fullpath: /
+ path: /
+ TEXT
+
+ get("/")
+ assert_equal expected.split("\n").map(&:strip),
+ last_response.body.split("\n").map(&:strip)
+ end
+
+ test "paths are properly generated when application is mounted at sub-path" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ app_file "app/controllers/bar_controller.rb", <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render text: bukkits.bukkit_path
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/bar' => 'bar#index', :as => 'bar'
+ mount Bukkits::Engine => "/bukkits", :as => "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ get '/bukkit' => 'bukkit#index'
+ end
+ RUBY
+
+
+ @plugin.write "app/controllers/bukkits/bukkit_controller.rb", <<-RUBY
+ class Bukkits::BukkitController < ActionController::Base
+ def index
+ render text: main_app.bar_path
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ get("/bukkits/bukkit", {}, {'SCRIPT_NAME' => '/foo'})
+ assert_equal '/foo/bar', last_response.body
+
+ get("/bar", {}, {'SCRIPT_NAME' => '/foo'})
+ assert_equal '/foo/bukkits/bukkit', last_response.body
+ end
+
+ test "paths are properly generated when application is mounted at sub-path and relative_url_root is set" do
+ add_to_config "config.relative_url_root = '/foo'"
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ app_file "app/controllers/bar_controller.rb", <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render text: bukkits.bukkit_path
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get '/bar' => 'bar#index', :as => 'bar'
+ mount Bukkits::Engine => "/bukkits", :as => "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ get '/bukkit' => 'bukkit#index'
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/bukkit_controller.rb", <<-RUBY
+ class Bukkits::BukkitController < ActionController::Base
+ def index
+ render text: main_app.bar_path
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ get("/bukkits/bukkit", {}, {'SCRIPT_NAME' => '/foo'})
+ assert_equal '/foo/bar', last_response.body
+
+ get("/bar", {}, {'SCRIPT_NAME' => '/foo'})
+ assert_equal '/foo/bukkits/bukkit', last_response.body
+ end
+
+ private
+ def app
+ Rails.application
+ end
+ end
+end
diff --git a/railties/test/railties/generators_test.rb b/railties/test/railties/generators_test.rb
new file mode 100644
index 0000000000..5f4171d44b
--- /dev/null
+++ b/railties/test/railties/generators_test.rb
@@ -0,0 +1,134 @@
+RAILS_ISOLATED_ENGINE = true
+require "isolation/abstract_unit"
+
+require 'generators/generators_test_helper'
+require "rails/generators/test_case"
+
+module RailtiesTests
+ class GeneratorTest < Rails::Generators::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def destination_root
+ tmp_path 'foo_bar'
+ end
+
+ def tmp_path(*args)
+ @tmp_path ||= File.realpath(Dir.mktmpdir)
+ File.join(@tmp_path, *args)
+ end
+
+ def engine_path
+ tmp_path('foo_bar')
+ end
+
+ def bundled_rails(cmd)
+ `bundle exec rails #{cmd}`
+ end
+
+ def rails(cmd)
+ environment = File.expand_path('../../../../load_paths', __FILE__)
+ if File.exist?("#{environment}.rb")
+ require_environment = "-r #{environment}"
+ end
+ `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails #{cmd}`
+ end
+
+ def build_engine(is_mountable=false)
+ FileUtils.rm_rf(engine_path)
+ FileUtils.mkdir_p(engine_path)
+
+ mountable = is_mountable ? "--mountable" : ""
+
+ rails("plugin new #{engine_path} --full #{mountable}")
+
+ Dir.chdir(engine_path) do
+ File.open("Gemfile", "w") do |f|
+ f.write <<-GEMFILE.gsub(/^ {12}/, '')
+ source "https://rubygems.org"
+
+ gem 'rails', path: '#{RAILS_FRAMEWORK_ROOT}'
+ gem 'sqlite3'
+ GEMFILE
+ end
+ end
+ end
+
+ def build_mountable_engine
+ build_engine(true)
+ end
+
+ def test_controllers_are_correctly_namespaced_when_engine_is_mountable
+ build_mountable_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g controller topics")
+ assert_file "app/controllers/foo_bar/topics_controller.rb", /module FooBar\n class TopicsController/
+ assert_no_file "app/controllers/topics_controller.rb"
+ end
+ end
+
+ def test_models_are_correctly_namespaced_when_engine_is_mountable
+ build_mountable_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g model topic")
+ assert_file "app/models/foo_bar/topic.rb", /module FooBar\n class Topic/
+ assert_no_file "app/models/topic.rb"
+ end
+ end
+
+ def test_table_name_prefix_is_correctly_namespaced_when_engine_is_mountable
+ build_mountable_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g model namespaced/topic")
+ assert_file "app/models/foo_bar/namespaced.rb", /module FooBar\n module Namespaced/ do |content|
+ assert_class_method :table_name_prefix, content do |method_content|
+ assert_match(/'foo_bar_namespaced_'/, method_content)
+ end
+ end
+ end
+ end
+
+ def test_helpers_are_correctly_namespaced_when_engine_is_mountable
+ build_mountable_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g helper topics")
+ assert_file "app/helpers/foo_bar/topics_helper.rb", /module FooBar\n module TopicsHelper/
+ assert_no_file "app/helpers/topics_helper.rb"
+ end
+ end
+
+ def test_controllers_are_not_namespaced_when_engine_is_not_mountable
+ build_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g controller topics")
+ assert_file "app/controllers/topics_controller.rb", /class TopicsController/
+ assert_no_file "app/controllers/foo_bar/topics_controller.rb"
+ end
+ end
+
+ def test_models_are_not_namespaced_when_engine_is_not_mountable
+ build_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g model topic")
+ assert_file "app/models/topic.rb", /class Topic/
+ assert_no_file "app/models/foo_bar/topic.rb"
+ end
+ end
+
+ def test_helpers_are_not_namespaced_when_engine_is_not_mountable
+ build_engine
+ Dir.chdir(engine_path) do
+ bundled_rails("g helper topics")
+ assert_file "app/helpers/topics_helper.rb", /module TopicsHelper/
+ assert_no_file "app/helpers/foo_bar/topics_helper.rb"
+ end
+ end
+
+ def test_assert_file_with_special_characters
+ path = "#{app_path}/tmp"
+ file_name = "#{path}/v0.1.4~alpha+nightly"
+ FileUtils.mkdir_p path
+ FileUtils.touch file_name
+ assert_file file_name
+ end
+ end
+end
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
new file mode 100644
index 0000000000..fb2071c7c3
--- /dev/null
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -0,0 +1,274 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class ApplicationRoutingTest < ActiveSupport::TestCase
+ require 'rack/test'
+ include Rack::Test::Methods
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ @simple_plugin = engine "weblog"
+ @plugin = engine "blog"
+ @metrics_plugin = engine "metrics"
+
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ mount Weblog::Engine, :at => '/', :as => 'weblog'
+ resources :posts
+ get "/engine_route" => "application_generating#engine_route"
+ get "/engine_route_in_view" => "application_generating#engine_route_in_view"
+ get "/weblog_engine_route" => "application_generating#weblog_engine_route"
+ get "/weblog_engine_route_in_view" => "application_generating#weblog_engine_route_in_view"
+ get "/url_for_engine_route" => "application_generating#url_for_engine_route"
+ get "/polymorphic_route" => "application_generating#polymorphic_route"
+ get "/application_polymorphic_path" => "application_generating#application_polymorphic_path"
+ scope "/:user", :user => "anonymous" do
+ mount Blog::Engine => "/blog"
+ end
+ mount Metrics::Engine => "/metrics"
+ root :to => 'main#index'
+ end
+ RUBY
+
+
+ @simple_plugin.write "lib/weblog.rb", <<-RUBY
+ module Weblog
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ @simple_plugin.write "config/routes.rb", <<-RUBY
+ Weblog::Engine.routes.draw do
+ get '/weblog' => "weblogs#index", as: 'weblogs'
+ end
+ RUBY
+
+ @simple_plugin.write "app/controllers/weblogs_controller.rb", <<-RUBY
+ class WeblogsController < ActionController::Base
+ def index
+ render text: request.url
+ end
+ end
+ RUBY
+
+ @metrics_plugin.write "lib/metrics.rb", <<-RUBY
+ module Metrics
+ class Engine < ::Rails::Engine
+ isolate_namespace(Metrics)
+ end
+ end
+ RUBY
+
+ @metrics_plugin.write "config/routes.rb", <<-RUBY
+ Metrics::Engine.routes.draw do
+ get '/generate_blog_route', to: 'generating#generate_blog_route'
+ get '/generate_blog_route_in_view', to: 'generating#generate_blog_route_in_view'
+ end
+ RUBY
+
+ @metrics_plugin.write "app/controllers/metrics/generating_controller.rb", <<-RUBY
+ module Metrics
+ class GeneratingController < ActionController::Base
+ def generate_blog_route
+ render text: blog.post_path(1)
+ end
+
+ def generate_blog_route_in_view
+ render inline: "<%= blog.post_path(1) -%>"
+ end
+ end
+ end
+ RUBY
+
+ @plugin.write "app/models/blog/post.rb", <<-RUBY
+ module Blog
+ class Post
+ include ActiveModel::Model
+
+ def id
+ 44
+ end
+
+ def persisted?
+ true
+ end
+ end
+ end
+ RUBY
+
+ @plugin.write "lib/blog.rb", <<-RUBY
+ module Blog
+ class Engine < ::Rails::Engine
+ isolate_namespace(Blog)
+ end
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Blog::Engine.routes.draw do
+ resources :posts
+ get '/generate_application_route', to: 'posts#generate_application_route'
+ get '/application_route_in_view', to: 'posts#application_route_in_view'
+ get '/engine_polymorphic_path', to: 'posts#engine_polymorphic_path'
+ get '/engine_asset_path', to: 'posts#engine_asset_path'
+ end
+ RUBY
+
+ @plugin.write "app/controllers/blog/posts_controller.rb", <<-RUBY
+ module Blog
+ class PostsController < ActionController::Base
+ def index
+ render text: blog.post_path(1)
+ end
+
+ def generate_application_route
+ path = main_app.url_for(controller: "/main",
+ action: "index",
+ only_path: true)
+ render text: path
+ end
+
+ def application_route_in_view
+ render inline: "<%= main_app.root_path %>"
+ end
+
+ def engine_polymorphic_path
+ render text: polymorphic_path(Post.new)
+ end
+
+ def engine_asset_path
+ render inline: "<%= asset_path 'images/foo.png' %>"
+ end
+ end
+ end
+ RUBY
+
+ app_file "app/controllers/application_generating_controller.rb", <<-RUBY
+ class ApplicationGeneratingController < ActionController::Base
+ def engine_route
+ render text: blog.posts_path
+ end
+
+ def engine_route_in_view
+ render inline: "<%= blog.posts_path %>"
+ end
+
+ def weblog_engine_route
+ render text: weblog.weblogs_path
+ end
+
+ def weblog_engine_route_in_view
+ render inline: "<%= weblog.weblogs_path %>"
+ end
+
+ def url_for_engine_route
+ render text: blog.url_for(controller: "blog/posts", action: "index", user: "john", only_path: true)
+ end
+
+ def polymorphic_route
+ render text: polymorphic_url([blog, Blog::Post.new])
+ end
+
+ def application_polymorphic_path
+ render text: polymorphic_path(Blog::Post.new)
+ end
+ end
+ RUBY
+
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= begin
+ require "#{app_path}/config/environment"
+ Rails.application
+ end
+ end
+
+ test "routes generation in engine and application" do
+ # test generating engine's route from engine
+ get "/john/blog/posts"
+ assert_equal "/john/blog/posts/1", last_response.body
+
+ # test generating engine's route from engine with default_url_options
+ get "/john/blog/posts", {}, 'SCRIPT_NAME' => "/foo"
+ assert_equal "/foo/john/blog/posts/1", last_response.body
+
+ # test generating engine's route from application
+ get "/engine_route"
+ assert_equal "/anonymous/blog/posts", last_response.body
+
+ get "/engine_route_in_view"
+ assert_equal "/anonymous/blog/posts", last_response.body
+
+ get "/url_for_engine_route"
+ assert_equal "/john/blog/posts", last_response.body
+
+ # test generating engine's route from application with default_url_options
+ get "/engine_route", {}, 'SCRIPT_NAME' => "/foo"
+ assert_equal "/foo/anonymous/blog/posts", last_response.body
+
+ get "/url_for_engine_route", {}, 'SCRIPT_NAME' => "/foo"
+ assert_equal "/foo/john/blog/posts", last_response.body
+
+ # test generating application's route from engine
+ get "/someone/blog/generate_application_route"
+ assert_equal "/", last_response.body
+
+ get "/somone/blog/application_route_in_view"
+ assert_equal "/", last_response.body
+
+ # test generating engine's route from other engine
+ get "/metrics/generate_blog_route"
+ assert_equal '/anonymous/blog/posts/1', last_response.body
+
+ get "/metrics/generate_blog_route_in_view"
+ assert_equal '/anonymous/blog/posts/1', last_response.body
+
+ # test generating engine's route from other engine with default_url_options
+ get "/metrics/generate_blog_route", {}, 'SCRIPT_NAME' => '/foo'
+ assert_equal '/foo/anonymous/blog/posts/1', last_response.body
+
+ get "/metrics/generate_blog_route_in_view", {}, 'SCRIPT_NAME' => '/foo'
+ assert_equal '/foo/anonymous/blog/posts/1', last_response.body
+
+
+ # test generating application's route from engine with default_url_options
+ get "/someone/blog/generate_application_route", {}, 'SCRIPT_NAME' => '/foo'
+ assert_equal "/foo/", last_response.body
+
+ # test polymorphic routes
+ get "/polymorphic_route"
+ assert_equal "http://example.org/anonymous/blog/posts/44", last_response.body
+
+ # test that correct path is generated for the same polymorphic_path call in an engine
+ get "/somone/blog/engine_polymorphic_path"
+ assert_equal "/somone/blog/posts/44", last_response.body
+
+ # and in an application
+ get "/application_polymorphic_path"
+ assert_equal "/posts/44", last_response.body
+
+ # test that asset path will not get script_name when generated in the engine
+ get "/someone/blog/engine_asset_path"
+ assert_equal "/images/foo.png", last_response.body
+ end
+
+ test "route path for controller action when engine is mounted at root" do
+ get "/weblog_engine_route"
+ assert_equal "/weblog", last_response.body
+
+ get "/weblog_engine_route_in_view"
+ assert_equal "/weblog", last_response.body
+ end
+ end
+end
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
new file mode 100644
index 0000000000..5042d628cf
--- /dev/null
+++ b/railties/test/railties/railtie_test.rb
@@ -0,0 +1,208 @@
+require "isolation/abstract_unit"
+
+module RailtiesTest
+ class RailtieTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ require "rails/all"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "cannot instantiate a Railtie object" do
+ assert_raise(RuntimeError) { Rails::Railtie.new }
+ end
+
+ test "Railtie provides railtie_name" do
+ begin
+ class ::FooBarBaz < Rails::Railtie ; end
+ assert_equal "foo_bar_baz", FooBarBaz.railtie_name
+ ensure
+ Object.send(:remove_const, :"FooBarBaz")
+ end
+ end
+
+ test "railtie_name can be set manually" do
+ class Foo < Rails::Railtie
+ railtie_name "bar"
+ end
+ assert_equal "bar", Foo.railtie_name
+ end
+
+ test "config is available to railtie" do
+ class Foo < Rails::Railtie ; end
+ assert_nil Foo.config.action_controller.foo
+ end
+
+ test "config name is available for the railtie" do
+ class Foo < Rails::Railtie
+ config.foo = ActiveSupport::OrderedOptions.new
+ config.foo.greetings = "hello"
+ end
+ assert_equal "hello", Foo.config.foo.greetings
+ end
+
+ test "railtie configurations are available in the application" do
+ class Foo < Rails::Railtie
+ config.foo = ActiveSupport::OrderedOptions.new
+ config.foo.greetings = "hello"
+ end
+ require "#{app_path}/config/application"
+ assert_equal "hello", Rails.application.config.foo.greetings
+ end
+
+ test "railtie can add to_prepare callbacks" do
+ $to_prepare = false
+ class Foo < Rails::Railtie ; config.to_prepare { $to_prepare = true } ; end
+ assert !$to_prepare
+ require "#{app_path}/config/environment"
+ require "rack/test"
+ extend Rack::Test::Methods
+ get "/"
+ assert $to_prepare
+ end
+
+ test "railtie have access to application in before_configuration callbacks" do
+ $before_configuration = false
+ class Foo < Rails::Railtie ; config.before_configuration { $before_configuration = Rails.root.to_path } ; end
+ assert_not $before_configuration
+ require "#{app_path}/config/environment"
+ assert_equal app_path, $before_configuration
+ end
+
+ test "railtie can add after_initialize callbacks" do
+ $after_initialize = false
+ class Foo < Rails::Railtie ; config.after_initialize { $after_initialize = true } ; end
+ assert !$after_initialize
+ require "#{app_path}/config/environment"
+ assert $after_initialize
+ end
+
+ test "rake_tasks block is executed when MyApp.load_tasks is called" do
+ $ran_block = false
+
+ class MyTie < Rails::Railtie
+ rake_tasks do
+ $ran_block = true
+ end
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert !$ran_block
+ require 'rake'
+ require 'rake/testtask'
+ require 'rdoc/task'
+
+ Rails.application.load_tasks
+ assert $ran_block
+ end
+
+ test "rake_tasks block defined in superclass of railtie is also executed" do
+ $ran_block = []
+
+ class Rails::Railtie
+ rake_tasks do
+ $ran_block << railtie_name
+ end
+ end
+
+ class MyTie < Rails::Railtie
+ railtie_name "my_tie"
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert_equal [], $ran_block
+ require 'rake'
+ require 'rake/testtask'
+ require 'rdoc/task'
+
+ Rails.application.load_tasks
+ assert $ran_block.include?("my_tie")
+ end
+
+ test "generators block is executed when MyApp.load_generators is called" do
+ $ran_block = false
+
+ class MyTie < Rails::Railtie
+ generators do
+ $ran_block = true
+ end
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert !$ran_block
+ Rails.application.load_generators
+ assert $ran_block
+ end
+
+ test "console block is executed when MyApp.load_console is called" do
+ $ran_block = false
+
+ class MyTie < Rails::Railtie
+ console do
+ $ran_block = true
+ end
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert !$ran_block
+ Rails.application.load_console
+ assert $ran_block
+ end
+
+ test "runner block is executed when MyApp.load_runner is called" do
+ $ran_block = false
+
+ class MyTie < Rails::Railtie
+ runner do
+ $ran_block = true
+ end
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert !$ran_block
+ Rails.application.load_runner
+ assert $ran_block
+ end
+
+ test "railtie can add initializers" do
+ $ran_block = false
+
+ class MyTie < Rails::Railtie
+ initializer :something_nice do
+ $ran_block = true
+ end
+ end
+
+ assert !$ran_block
+ require "#{app_path}/config/environment"
+ assert $ran_block
+ end
+
+ test "we can change our environment if we want to" do
+ begin
+ original_env = Rails.env
+ Rails.env = 'foo'
+ assert_equal('foo', Rails.env)
+ ensure
+ Rails.env = original_env
+ assert_equal(original_env, Rails.env)
+ end
+ end
+ end
+end
diff --git a/railties/test/test_unit/reporter_test.rb b/railties/test/test_unit/reporter_test.rb
new file mode 100644
index 0000000000..e517d8dd0b
--- /dev/null
+++ b/railties/test/test_unit/reporter_test.rb
@@ -0,0 +1,147 @@
+require 'abstract_unit'
+require 'rails/test_unit/reporter'
+
+class TestUnitReporterTest < ActiveSupport::TestCase
+ class ExampleTest < Minitest::Test
+ def woot; end
+ end
+
+ setup do
+ @output = StringIO.new
+ @reporter = Rails::TestUnitReporter.new @output, output_inline: true
+ end
+
+ test "prints rerun snippet to run a single failed test" do
+ @reporter.record(failed_test)
+ @reporter.report
+
+ assert_match %r{^bin/rails test .*test/test_unit/reporter_test.rb:\d+$}, @output.string
+ assert_rerun_snippet_count 1
+ end
+
+ test "prints rerun snippet for every failed test" do
+ @reporter.record(failed_test)
+ @reporter.record(failed_test)
+ @reporter.record(failed_test)
+ @reporter.report
+
+ assert_rerun_snippet_count 3
+ end
+
+ test "does not print snippet for successful and skipped tests" do
+ @reporter.record(passing_test)
+ @reporter.record(skipped_test)
+ @reporter.report
+ assert_no_match 'Failed tests:', @output.string
+ assert_rerun_snippet_count 0
+ end
+
+ test "prints rerun snippet for skipped tests if run in verbose mode" do
+ verbose = Rails::TestUnitReporter.new @output, verbose: true
+ verbose.record(skipped_test)
+ verbose.report
+
+ assert_rerun_snippet_count 1
+ end
+
+ test "allows to customize the executable in the rerun snippet" do
+ original_executable = Rails::TestUnitReporter.executable
+ begin
+ Rails::TestUnitReporter.executable = "bin/test"
+ @reporter.record(failed_test)
+ @reporter.report
+
+ assert_match %r{^bin/test .*test/test_unit/reporter_test.rb:\d+$}, @output.string
+ ensure
+ Rails::TestUnitReporter.executable = original_executable
+ end
+ end
+
+ test "outputs failures inline" do
+ @reporter.record(failed_test)
+ @reporter.report
+
+ assert_match %r{\A\n\nboo\n\nbin/rails test .*test/test_unit/reporter_test.rb:\d+\n\n\z}, @output.string
+ end
+
+ test "outputs errors inline" do
+ @reporter.record(errored_test)
+ @reporter.report
+
+ assert_match %r{\A\n\nArgumentError: wups\n No backtrace\n\nbin/rails test .*test/test_unit/reporter_test.rb:6\n\n\z}, @output.string
+ end
+
+ test "outputs skipped tests inline if verbose" do
+ verbose = Rails::TestUnitReporter.new @output, verbose: true, output_inline: true
+ verbose.record(skipped_test)
+ verbose.report
+
+ assert_match %r{\A\n\nskipchurches, misstemples\n\nbin/rails test .*test/test_unit/reporter_test.rb:\d+\n\n\z}, @output.string
+ end
+
+ test "does not output rerun snippets after run" do
+ @reporter.record(failed_test)
+ @reporter.report
+
+ assert_no_match 'Failed tests:', @output.string
+ end
+
+ test "fail fast interrupts run on failure" do
+ fail_fast = Rails::TestUnitReporter.new @output, fail_fast: true
+ interrupt_raised = false
+
+ # Minitest passes through Interrupt, catch it manually.
+ begin
+ fail_fast.record(failed_test)
+ rescue Interrupt
+ interrupt_raised = true
+ ensure
+ assert interrupt_raised, 'Expected Interrupt to be raised.'
+ end
+ end
+
+ test "fail fast does not interrupt run errors or skips" do
+ fail_fast = Rails::TestUnitReporter.new @output, fail_fast: true
+
+ fail_fast.record(errored_test)
+ assert_no_match 'Failed tests:', @output.string
+
+ fail_fast.record(skipped_test)
+ assert_no_match 'Failed tests:', @output.string
+ end
+
+ private
+ def assert_rerun_snippet_count(snippet_count)
+ assert_equal snippet_count, @output.string.scan(%r{^bin/rails test }).size
+ end
+
+ def failed_test
+ ft = ExampleTest.new(:woot)
+ ft.failures << begin
+ raise Minitest::Assertion, "boo"
+ rescue Minitest::Assertion => e
+ e
+ end
+ ft
+ end
+
+ def errored_test
+ et = ExampleTest.new(:woot)
+ et.failures << Minitest::UnexpectedError.new(ArgumentError.new("wups"))
+ et
+ end
+
+ def passing_test
+ ExampleTest.new(:woot)
+ end
+
+ def skipped_test
+ st = ExampleTest.new(:woot)
+ st.failures << begin
+ raise Minitest::Skip, "skipchurches, misstemples"
+ rescue Minitest::Assertion => e
+ e
+ end
+ st
+ end
+end
diff --git a/railties/test/version_test.rb b/railties/test/version_test.rb
new file mode 100644
index 0000000000..f270d8f0c9
--- /dev/null
+++ b/railties/test/version_test.rb
@@ -0,0 +1,12 @@
+require 'abstract_unit'
+
+class VersionTest < ActiveSupport::TestCase
+ def test_rails_version_returns_a_string
+ assert Rails.version.is_a? String
+ end
+
+ def test_rails_gem_version_returns_a_correct_gem_version_object
+ assert Rails.gem_version.is_a? Gem::Version
+ assert_equal Rails.version, Rails.gem_version.to_s
+ end
+end