railties 7.1.3.4 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +128 -775
- data/lib/minitest/rails_plugin.rb +6 -3
- data/lib/rails/all.rb +1 -3
- data/lib/rails/api/task.rb +6 -4
- data/lib/rails/application/bootstrap.rb +5 -7
- data/lib/rails/application/configuration.rb +81 -43
- data/lib/rails/application/default_middleware_stack.rb +4 -0
- data/lib/rails/application/dummy_config.rb +2 -2
- data/lib/rails/application/finisher.rb +9 -3
- data/lib/rails/application/routes_reloader.rb +16 -2
- data/lib/rails/application.rb +27 -86
- data/lib/rails/backtrace_cleaner.rb +19 -4
- data/lib/rails/cli.rb +0 -1
- data/lib/rails/code_statistics.rb +128 -86
- data/lib/rails/code_statistics_calculator.rb +78 -76
- data/lib/rails/command/helpers/editor.rb +1 -1
- data/lib/rails/command.rb +0 -6
- data/lib/rails/commands/app/update_command.rb +94 -0
- data/lib/rails/commands/boot/boot_command.rb +14 -0
- data/lib/rails/commands/console/console_command.rb +2 -21
- data/lib/rails/commands/console/irb_console.rb +128 -0
- data/lib/rails/commands/credentials/USAGE +4 -4
- data/lib/rails/commands/credentials/credentials_command.rb +7 -3
- data/lib/rails/commands/dbconsole/dbconsole_command.rb +21 -30
- data/lib/rails/commands/dev/dev_command.rb +1 -1
- data/lib/rails/commands/devcontainer/devcontainer_command.rb +40 -0
- data/lib/rails/commands/rake/rake_command.rb +1 -1
- data/lib/rails/commands/runner/runner_command.rb +14 -3
- data/lib/rails/commands/server/server_command.rb +5 -3
- data/lib/rails/commands/stats/stats_command.rb +19 -0
- data/lib/rails/commands/test/test_command.rb +2 -0
- data/lib/rails/configuration.rb +10 -1
- data/lib/rails/console/methods.rb +7 -0
- data/lib/rails/dev_caching.rb +2 -2
- data/lib/rails/engine/configuration.rb +3 -1
- data/lib/rails/engine/lazy_route_set.rb +114 -0
- data/lib/rails/engine.rb +16 -12
- data/lib/rails/gem_version.rb +4 -4
- data/lib/rails/generators/actions.rb +3 -3
- data/lib/rails/generators/app_base.rb +115 -68
- data/lib/rails/generators/base.rb +1 -1
- data/lib/rails/generators/database.rb +263 -71
- data/lib/rails/generators/erb/authentication/authentication_generator.rb +15 -0
- data/lib/rails/generators/erb/authentication/templates/app/views/passwords/edit.html.erb +9 -0
- data/lib/rails/generators/erb/authentication/templates/app/views/passwords/new.html.erb +8 -0
- data/lib/rails/generators/erb/authentication/templates/app/views/sessions/new.html.erb +11 -0
- data/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt +2 -0
- data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +2 -0
- data/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt +2 -0
- data/lib/rails/generators/generated_attribute.rb +42 -12
- data/lib/rails/generators/migration.rb +3 -3
- data/lib/rails/generators/rails/app/app_generator.rb +75 -54
- data/lib/rails/generators/rails/app/templates/Dockerfile.tt +33 -17
- data/lib/rails/generators/rails/app/templates/Gemfile.tt +38 -23
- data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt +6 -11
- data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +4 -0
- data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +17 -3
- data/lib/rails/generators/rails/app/templates/app/views/pwa/manifest.json.erb.tt +22 -0
- data/lib/rails/generators/rails/app/templates/app/views/pwa/service-worker.js +26 -0
- data/lib/rails/generators/rails/app/templates/bin/brakeman.tt +6 -0
- data/lib/rails/generators/rails/app/templates/bin/dev.tt +1 -0
- data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +7 -0
- data/lib/rails/generators/rails/app/templates/bin/setup.tt +6 -5
- data/lib/rails/generators/rails/app/templates/bin/thrust.tt +4 -0
- data/lib/rails/generators/rails/app/templates/config/application.rb.tt +1 -1
- data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +26 -3
- data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +30 -0
- data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +47 -0
- data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +26 -3
- data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +128 -0
- data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +22 -26
- data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +36 -48
- data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +7 -18
- data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +0 -7
- data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +1 -1
- data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +30 -0
- data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +35 -27
- data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +6 -0
- data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +7 -1
- data/lib/rails/generators/rails/app/templates/dockerignore.tt +20 -2
- data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +144 -0
- data/lib/rails/generators/rails/app/templates/github/dependabot.yml +12 -0
- data/lib/rails/generators/rails/app/templates/gitignore.tt +4 -5
- data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +17 -0
- data/lib/rails/generators/rails/app/templates/public/400.html +114 -0
- data/lib/rails/generators/rails/app/templates/public/404.html +113 -66
- data/lib/rails/generators/rails/app/templates/public/406-unsupported-browser.html +114 -0
- data/lib/rails/generators/rails/app/templates/public/422.html +113 -66
- data/lib/rails/generators/rails/app/templates/public/500.html +113 -65
- data/lib/rails/generators/rails/app/templates/public/icon.png +0 -0
- data/lib/rails/generators/rails/app/templates/public/icon.svg +3 -0
- data/lib/rails/generators/rails/app/templates/rubocop.yml.tt +8 -0
- data/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb.tt +1 -1
- data/lib/rails/generators/rails/authentication/USAGE +6 -0
- data/lib/rails/generators/rails/authentication/authentication_generator.rb +58 -0
- data/lib/rails/generators/rails/authentication/templates/app/channels/application_cable/connection.rb.tt +16 -0
- data/lib/rails/generators/rails/authentication/templates/app/controllers/concerns/authentication.rb.tt +52 -0
- data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt +33 -0
- data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt +21 -0
- data/lib/rails/generators/rails/authentication/templates/app/mailers/passwords_mailer.rb.tt +6 -0
- data/lib/rails/generators/rails/authentication/templates/app/models/current.rb.tt +4 -0
- data/lib/rails/generators/rails/authentication/templates/app/models/session.rb.tt +3 -0
- data/lib/rails/generators/rails/authentication/templates/app/models/user.rb.tt +6 -0
- data/lib/rails/generators/rails/authentication/templates/app/views/passwords_mailer/reset.html.erb.tt +4 -0
- data/lib/rails/generators/rails/authentication/templates/app/views/passwords_mailer/reset.text.erb.tt +2 -0
- data/lib/rails/generators/rails/authentication/templates/test/mailers/previews/passwords_mailer_preview.rb.tt +7 -0
- data/lib/rails/generators/rails/controller/controller_generator.rb +1 -1
- data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +4 -0
- data/lib/rails/generators/rails/db/system/change/change_generator.rb +132 -21
- data/lib/rails/generators/rails/devcontainer/devcontainer_generator.rb +176 -0
- data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +3 -0
- data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +47 -0
- data/lib/rails/generators/rails/devcontainer/templates/devcontainer/devcontainer.json.tt +37 -0
- data/lib/rails/generators/rails/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/rails/plugin/plugin_generator.rb +47 -16
- data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +2 -2
- data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +6 -2
- data/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt +2 -0
- data/lib/rails/generators/rails/plugin/templates/bin/rubocop.tt +7 -0
- data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +103 -0
- data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +12 -0
- data/lib/rails/generators/rails/plugin/templates/rubocop.yml.tt +8 -0
- data/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb.tt +1 -1
- data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +2 -2
- data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +2 -2
- data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +3 -3
- data/lib/rails/generators/rails/script/USAGE +18 -0
- data/lib/rails/generators/rails/script/script_generator.rb +18 -0
- data/lib/rails/generators/rails/script/templates/script.rb.tt +3 -0
- data/lib/rails/generators/test_unit/authentication/authentication_generator.rb +14 -0
- data/lib/rails/generators/test_unit/authentication/templates/test/fixtures/users.yml.tt +9 -0
- data/lib/rails/generators/test_unit/authentication/templates/test/models/user_test.rb.tt +7 -0
- data/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt +6 -4
- data/lib/rails/generators/test_unit/mailer/templates/preview.rb.tt +3 -2
- data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +15 -1
- data/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt +2 -2
- data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt +2 -2
- data/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt +2 -0
- data/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb.tt +1 -1
- data/lib/rails/generators/testing/assertions.rb +20 -0
- data/lib/rails/generators/testing/behavior.rb +7 -6
- data/lib/rails/generators.rb +7 -2
- data/lib/rails/health_controller.rb +1 -1
- data/lib/rails/info.rb +2 -2
- data/lib/rails/info_controller.rb +10 -2
- data/lib/rails/mailers_controller.rb +14 -1
- data/lib/rails/paths.rb +2 -2
- data/lib/rails/pwa_controller.rb +15 -0
- data/lib/rails/rack/logger.rb +15 -7
- data/lib/rails/rack/silence_request.rb +33 -0
- data/lib/rails/rack.rb +1 -0
- data/lib/rails/railtie/configurable.rb +2 -2
- data/lib/rails/railtie.rb +15 -16
- data/lib/rails/source_annotation_extractor.rb +31 -14
- data/lib/rails/tasks/framework.rake +0 -26
- data/lib/rails/tasks/statistics.rake +13 -28
- data/lib/rails/tasks/tmp.rake +1 -1
- data/lib/rails/templates/layouts/application.html.erb +1 -1
- data/lib/rails/templates/rails/info/notes.html.erb +65 -0
- data/lib/rails/templates/rails/mailers/email.html.erb +12 -8
- data/lib/rails/templates/rails/welcome/index.html.erb +4 -2
- data/lib/rails/test_help.rb +2 -4
- data/lib/rails/test_unit/reporter.rb +8 -2
- data/lib/rails/test_unit/runner.rb +27 -2
- data/lib/rails/test_unit/test_parser.rb +48 -0
- data/lib/rails.rb +7 -4
- metadata +77 -45
- data/lib/rails/app_updater.rb +0 -40
- data/lib/rails/commands/secrets/USAGE +0 -61
- data/lib/rails/commands/secrets/secrets_command.rb +0 -47
- data/lib/rails/console/app.rb +0 -35
- data/lib/rails/console/helpers.rb +0 -19
- data/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt +0 -2
- data/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt +0 -4
- data/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt +0 -4
- data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +0 -68
- data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +0 -54
- data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +0 -70
- data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +0 -24
- data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +0 -62
- data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +0 -53
- data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +0 -284
- data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +0 -13
- data/lib/rails/generators/rails/app/templates/public/apple-touch-icon-precomposed.png +0 -0
- data/lib/rails/generators/rails/app/templates/public/apple-touch-icon.png +0 -0
- data/lib/rails/generators/rails/app/templates/public/favicon.ico +0 -0
- data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +0 -13
- data/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt +0 -10
- data/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt +0 -6
- data/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt +0 -17
- data/lib/rails/ruby_version_check.rb +0 -17
- data/lib/rails/secrets.rb +0 -110
data/lib/rails/rack/logger.rb
CHANGED
@@ -20,22 +20,26 @@ module Rails
|
|
20
20
|
def call(env)
|
21
21
|
request = ActionDispatch::Request.new(env)
|
22
22
|
|
23
|
-
if logger.respond_to?(:
|
24
|
-
logger.
|
23
|
+
env["rails.rack_logger_tag_count"] = if logger.respond_to?(:push_tags)
|
24
|
+
logger.push_tags(*compute_tags(request)).size
|
25
25
|
else
|
26
|
-
|
26
|
+
0
|
27
27
|
end
|
28
|
+
|
29
|
+
call_app(request, env)
|
28
30
|
end
|
29
31
|
|
30
32
|
private
|
31
33
|
def call_app(request, env) # :doc:
|
34
|
+
logger_tag_pop_count = env["rails.rack_logger_tag_count"]
|
35
|
+
|
32
36
|
instrumenter = ActiveSupport::Notifications.instrumenter
|
33
37
|
handle = instrumenter.build_handle("request.action_dispatch", { request: request })
|
34
38
|
handle.start
|
35
39
|
|
36
40
|
logger.info { started_request_message(request) }
|
37
41
|
status, headers, body = response = @app.call(env)
|
38
|
-
body = ::Rack::BodyProxy.new(body,
|
42
|
+
body = ::Rack::BodyProxy.new(body) { finish_request_instrumentation(handle, logger_tag_pop_count) }
|
39
43
|
|
40
44
|
if response.frozen?
|
41
45
|
[status, headers, body]
|
@@ -44,10 +48,8 @@ module Rails
|
|
44
48
|
response
|
45
49
|
end
|
46
50
|
rescue Exception
|
47
|
-
handle
|
51
|
+
finish_request_instrumentation(handle, logger_tag_pop_count)
|
48
52
|
raise
|
49
|
-
ensure
|
50
|
-
ActiveSupport::LogSubscriber.flush_all!
|
51
53
|
end
|
52
54
|
|
53
55
|
# Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
|
@@ -75,6 +77,12 @@ module Rails
|
|
75
77
|
def logger
|
76
78
|
Rails.logger
|
77
79
|
end
|
80
|
+
|
81
|
+
def finish_request_instrumentation(handle, logger_tag_pop_count)
|
82
|
+
handle.finish
|
83
|
+
logger.pop_tags(logger_tag_pop_count) if logger.respond_to?(:pop_tags) && logger_tag_pop_count > 0
|
84
|
+
ActiveSupport::LogSubscriber.flush_all!
|
85
|
+
end
|
78
86
|
end
|
79
87
|
end
|
80
88
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "active_support/logger_silence"
|
6
|
+
|
7
|
+
module Rails
|
8
|
+
module Rack
|
9
|
+
# Allows you to silence requests made to a specific path.
|
10
|
+
# This is useful for preventing recurring requests like health checks from clogging the logging.
|
11
|
+
# This middleware is used to do just that against the path /up in production by default.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# config.middleware.insert_before \
|
16
|
+
# Rails::Rack::Logger, Rails::Rack::SilenceRequest, path: "/up"
|
17
|
+
#
|
18
|
+
# This middleware can also be configured using `config.silence_healthcheck_path = "/up"` in Rails.
|
19
|
+
class SilenceRequest
|
20
|
+
def initialize(app, path:)
|
21
|
+
@app, @path = app, path
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(env)
|
25
|
+
if env["PATH_INFO"] == @path
|
26
|
+
Rails.logger.silence { @app.call(env) }
|
27
|
+
else
|
28
|
+
@app.call(env)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/rails/rack.rb
CHANGED
data/lib/rails/railtie.rb
CHANGED
@@ -50,8 +50,8 @@ module Rails
|
|
50
50
|
# To add an initialization step to the \Rails boot process from your railtie, just
|
51
51
|
# define the initialization code with the +initializer+ macro:
|
52
52
|
#
|
53
|
-
# class
|
54
|
-
# initializer "
|
53
|
+
# class MyGem::Railtie < Rails::Railtie
|
54
|
+
# initializer "my_gem.configure_rails_initialization" do
|
55
55
|
# # some initialization behavior
|
56
56
|
# end
|
57
57
|
# end
|
@@ -59,9 +59,9 @@ module Rails
|
|
59
59
|
# If specified, the block can also receive the application object, in case you
|
60
60
|
# need to access some application-specific configuration, like middleware:
|
61
61
|
#
|
62
|
-
# class
|
63
|
-
# initializer "
|
64
|
-
# app.middleware.use
|
62
|
+
# class MyGem::Railtie < Rails::Railtie
|
63
|
+
# initializer "my_gem.configure_rails_initialization" do |app|
|
64
|
+
# app.middleware.use MyGem::Middleware
|
65
65
|
# end
|
66
66
|
# end
|
67
67
|
#
|
@@ -74,14 +74,14 @@ module Rails
|
|
74
74
|
# Railties can access a config object which contains configuration shared by all
|
75
75
|
# railties and the application:
|
76
76
|
#
|
77
|
-
# class
|
77
|
+
# class MyGem::Railtie < Rails::Railtie
|
78
78
|
# # Customize the ORM
|
79
|
-
# config.app_generators.orm :
|
79
|
+
# config.app_generators.orm :my_gem_orm
|
80
80
|
#
|
81
81
|
# # Add a to_prepare block which is executed once in production
|
82
82
|
# # and before each request in development.
|
83
83
|
# config.to_prepare do
|
84
|
-
#
|
84
|
+
# MyGem.setup!
|
85
85
|
# end
|
86
86
|
# end
|
87
87
|
#
|
@@ -90,9 +90,9 @@ module Rails
|
|
90
90
|
# If your railtie has Rake tasks, you can tell \Rails to load them through the method
|
91
91
|
# +rake_tasks+:
|
92
92
|
#
|
93
|
-
# class
|
93
|
+
# class MyGem::Railtie < Rails::Railtie
|
94
94
|
# rake_tasks do
|
95
|
-
# load "path/to/
|
95
|
+
# load "path/to/my_gem.tasks"
|
96
96
|
# end
|
97
97
|
# end
|
98
98
|
#
|
@@ -100,9 +100,9 @@ module Rails
|
|
100
100
|
# your generators at a different location, you can specify in your railtie a block which
|
101
101
|
# will load them during normal generators lookup:
|
102
102
|
#
|
103
|
-
# class
|
103
|
+
# class MyGem::Railtie < Rails::Railtie
|
104
104
|
# generators do
|
105
|
-
# require "path/to/
|
105
|
+
# require "path/to/my_gem_generator"
|
106
106
|
# end
|
107
107
|
# end
|
108
108
|
#
|
@@ -120,7 +120,7 @@ module Rails
|
|
120
120
|
# this less confusing for everyone.
|
121
121
|
# It can be used like this:
|
122
122
|
#
|
123
|
-
# class
|
123
|
+
# class MyGem::Railtie < Rails::Railtie
|
124
124
|
# server do
|
125
125
|
# WebpackServer.start
|
126
126
|
# end
|
@@ -221,14 +221,13 @@ module Rails
|
|
221
221
|
|
222
222
|
# If the class method does not have a method, then send the method call
|
223
223
|
# to the Railtie instance.
|
224
|
-
def method_missing(name,
|
224
|
+
def method_missing(name, ...)
|
225
225
|
if !abstract_railtie? && instance.respond_to?(name)
|
226
|
-
instance.public_send(name,
|
226
|
+
instance.public_send(name, ...)
|
227
227
|
else
|
228
228
|
super
|
229
229
|
end
|
230
230
|
end
|
231
|
-
ruby2_keywords(:method_missing)
|
232
231
|
|
233
232
|
# receives an instance variable identifier, set the variable value if is
|
234
233
|
# blank and append given block to value, which will be used later in
|
@@ -1,6 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
begin
|
4
|
+
require "prism"
|
5
|
+
rescue LoadError
|
6
|
+
# If Prism isn't available (because of using an older Ruby version) then we'll
|
7
|
+
# define a fallback for the ParserExtractor using ripper.
|
8
|
+
require "ripper"
|
9
|
+
end
|
4
10
|
|
5
11
|
module Rails
|
6
12
|
# Implements the logic behind +Rails::Command::NotesCommand+. See <tt>rails notes --help</tt> for usage information.
|
@@ -16,24 +22,35 @@ module Rails
|
|
16
22
|
# Wraps a regular expression that will be tested against each of the source
|
17
23
|
# file's comments.
|
18
24
|
class ParserExtractor < Struct.new(:pattern)
|
19
|
-
|
20
|
-
|
25
|
+
if defined?(Prism)
|
26
|
+
def annotations(file)
|
27
|
+
result = Prism.parse_file(file)
|
28
|
+
return [] unless result.success?
|
21
29
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@comments = []
|
30
|
+
result.comments.filter_map do |comment|
|
31
|
+
Annotation.new(comment.location.start_line, $1, $2) if comment.location.slice =~ pattern
|
32
|
+
end
|
26
33
|
end
|
34
|
+
else
|
35
|
+
class Parser < Ripper
|
36
|
+
attr_reader :comments, :pattern
|
37
|
+
|
38
|
+
def initialize(source, pattern:)
|
39
|
+
super(source)
|
40
|
+
@pattern = pattern
|
41
|
+
@comments = []
|
42
|
+
end
|
27
43
|
|
28
|
-
|
29
|
-
|
44
|
+
def on_comment(value)
|
45
|
+
@comments << Annotation.new(lineno, $1, $2) if value =~ pattern
|
46
|
+
end
|
30
47
|
end
|
31
|
-
end
|
32
48
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
49
|
+
def annotations(file)
|
50
|
+
contents = File.read(file, encoding: Encoding::BINARY)
|
51
|
+
parser = Parser.new(contents, pattern: pattern).tap(&:parse)
|
52
|
+
parser.error? ? [] : parser.comments
|
53
|
+
end
|
37
54
|
end
|
38
55
|
end
|
39
56
|
|
@@ -1,9 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
namespace :app do
|
4
|
-
desc "Update configs and some other initially generated files (or use just update:configs or update:bin)"
|
5
|
-
task update: [ "update:configs", "update:bin", "update:active_storage", "update:upgrade_guide_info" ]
|
6
|
-
|
7
4
|
desc "Apply the template supplied by LOCATION=(/path/to/template) or URL"
|
8
5
|
task template: :environment do
|
9
6
|
template = ENV["LOCATION"]
|
@@ -34,27 +31,4 @@ namespace :app do
|
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
37
|
-
|
38
|
-
namespace :update do
|
39
|
-
require "rails/app_updater"
|
40
|
-
|
41
|
-
# desc "Update config files from your current rails install"
|
42
|
-
task :configs do
|
43
|
-
Rails::AppUpdater.invoke_from_app_generator :create_boot_file
|
44
|
-
Rails::AppUpdater.invoke_from_app_generator :update_config_files
|
45
|
-
end
|
46
|
-
|
47
|
-
# desc "Add new executables to the application bin/ directory"
|
48
|
-
task :bin do
|
49
|
-
Rails::AppUpdater.invoke_from_app_generator :update_bin_files
|
50
|
-
end
|
51
|
-
|
52
|
-
task :active_storage do
|
53
|
-
Rails::AppUpdater.invoke_from_app_generator :update_active_storage
|
54
|
-
end
|
55
|
-
|
56
|
-
task :upgrade_guide_info do
|
57
|
-
Rails::AppUpdater.invoke_from_app_generator :display_upgrade_guide_info
|
58
|
-
end
|
59
|
-
end
|
60
34
|
end
|
@@ -1,32 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
STATS_DIRECTORIES
|
7
|
-
|
8
|
-
|
9
|
-
%w(Jobs app/jobs),
|
10
|
-
%w(Models app/models),
|
11
|
-
%w(Mailers app/mailers),
|
12
|
-
%w(Mailboxes app/mailboxes),
|
13
|
-
%w(Channels app/channels),
|
14
|
-
%w(Views app/views),
|
15
|
-
%w(JavaScripts app/assets/javascripts),
|
16
|
-
%w(Stylesheets app/assets/stylesheets),
|
17
|
-
%w(JavaScript app/javascript),
|
18
|
-
%w(Libraries lib/),
|
19
|
-
%w(APIs app/apis),
|
20
|
-
%w(Controller\ tests test/controllers),
|
21
|
-
%w(Helper\ tests test/helpers),
|
22
|
-
%w(Job\ tests test/jobs),
|
23
|
-
%w(Model\ tests test/models),
|
24
|
-
%w(Mailer\ tests test/mailers),
|
25
|
-
%w(Mailbox\ tests test/mailboxes),
|
26
|
-
%w(Channel\ tests test/channels),
|
27
|
-
%w(Integration\ tests test/integration),
|
28
|
-
%w(System\ tests test/system),
|
29
|
-
]
|
3
|
+
require "rails/code_statistics"
|
4
|
+
STATS_DIRECTORIES = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
|
5
|
+
Rails::CodeStatistics::DIRECTORIES,
|
6
|
+
"`STATS_DIRECTORIES` is deprecated and will be removed in Rails 8.1! Use `Rails::CodeStatistics.register_directory('My Directory', 'path/to/dir)` instead.",
|
7
|
+
Rails.deprecator
|
8
|
+
)
|
30
9
|
|
31
10
|
desc "Report code statistics (KLOCs, etc) from the application or engine"
|
32
11
|
task :stats do
|
@@ -34,5 +13,11 @@ task :stats do
|
|
34
13
|
stat_directories = STATS_DIRECTORIES.collect do |name, dir|
|
35
14
|
[ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
|
36
15
|
end.select { |name, dir| File.directory?(dir) }
|
37
|
-
|
16
|
+
|
17
|
+
$stderr.puts Rails.deprecator.warn(<<~MSG, caller_locations(0..1))
|
18
|
+
`bin/rake stats` has been deprecated and will be removed in Rails 8.1.
|
19
|
+
Please use `bin/rails stats` as Rails command instead.\n
|
20
|
+
MSG
|
21
|
+
|
22
|
+
Rails::CodeStatistics.new(*stat_directories).to_s
|
38
23
|
end
|
data/lib/rails/tasks/tmp.rake
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
<style>
|
2
|
+
h2, p {
|
3
|
+
padding-left: 30px;
|
4
|
+
}
|
5
|
+
table {
|
6
|
+
margin: 0;
|
7
|
+
border-collapse: collapse;
|
8
|
+
word-wrap:break-word;
|
9
|
+
table-layout: fixed;
|
10
|
+
width:100%;
|
11
|
+
}
|
12
|
+
table thead tr {
|
13
|
+
border-bottom: 2px solid #ddd;
|
14
|
+
}
|
15
|
+
table th {
|
16
|
+
padding-left: 30px;
|
17
|
+
text-align: left;
|
18
|
+
}
|
19
|
+
table thead th.tag, table thead th.line-no {
|
20
|
+
width: 10%;
|
21
|
+
}
|
22
|
+
table tbody tr {
|
23
|
+
border-bottom: 1px solid #ddd;
|
24
|
+
}
|
25
|
+
table tbody tr:nth-child(odd) {
|
26
|
+
background: #f2f2f2;
|
27
|
+
}
|
28
|
+
table td {
|
29
|
+
padding: 4px 30px;
|
30
|
+
}
|
31
|
+
@media (prefers-color-scheme: dark) {
|
32
|
+
table tbody tr:nth-child(odd) {
|
33
|
+
background: #282828;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
</style>
|
37
|
+
|
38
|
+
<h2>
|
39
|
+
Notes
|
40
|
+
</h2>
|
41
|
+
|
42
|
+
<table id="route_table" class="table">
|
43
|
+
<thead>
|
44
|
+
<th>File Name</th>
|
45
|
+
<th class="line-no">Line No.</th>
|
46
|
+
<th class="tag">Tag</th>
|
47
|
+
<th>Description</th>
|
48
|
+
</thead>
|
49
|
+
<tbody>
|
50
|
+
<% @annotations.each do |file, annotations| %>
|
51
|
+
<% annotations.each.with_index do |annotation, index| %>
|
52
|
+
<tr>
|
53
|
+
<% if index == 0 %>
|
54
|
+
<th rowspan="<%= annotations.size %>">
|
55
|
+
<%= file %>
|
56
|
+
</th>
|
57
|
+
<% end %>
|
58
|
+
<td class="line-no"><%= annotation.line %></td>
|
59
|
+
<td class="tag"><%= annotation.tag %></td>
|
60
|
+
<td><%= annotation.text %></td>
|
61
|
+
</tr>
|
62
|
+
<% end %>
|
63
|
+
<% end %>
|
64
|
+
</tbody>
|
65
|
+
</table>
|
@@ -61,14 +61,14 @@
|
|
61
61
|
<body>
|
62
62
|
<header>
|
63
63
|
<dl>
|
64
|
-
<% if
|
64
|
+
<% if Array(@email.from) != Array(@email.smtp_envelope_from) %>
|
65
65
|
<dt>SMTP-From:</dt>
|
66
66
|
<dd id="smtp_from"><%= @email.smtp_envelope_from %></dd>
|
67
67
|
<% end %>
|
68
68
|
|
69
|
-
<% if
|
69
|
+
<% if Set[*@email.to, *@email.cc, *@email.bcc] != Set[*@email.smtp_envelope_to] %>
|
70
70
|
<dt>SMTP-To:</dt>
|
71
|
-
<dd id="smtp_to"><%= @email.smtp_envelope_to %></dd>
|
71
|
+
<dd id="smtp_to"><%= @email.smtp_envelope_to.join(", ") %></dd>
|
72
72
|
<% end %>
|
73
73
|
|
74
74
|
<dt>From:</dt>
|
@@ -93,17 +93,21 @@
|
|
93
93
|
<% end %>
|
94
94
|
|
95
95
|
<dt>Date:</dt>
|
96
|
-
<dd id="date"><%= Time.current.rfc2822 %></dd>
|
96
|
+
<dd id="date"><%= @email.header['date'] || Time.current.rfc2822 %></dd>
|
97
97
|
|
98
98
|
<dt>Subject:</dt>
|
99
99
|
<dd><strong id="subject"><%= @email.subject %></strong></dd>
|
100
100
|
|
101
|
-
<%
|
101
|
+
<% if @attachments.any? || @inline_attachments.any? %>
|
102
102
|
<dt>Attachments:</dt>
|
103
103
|
<dd>
|
104
|
-
<% @
|
105
|
-
|
106
|
-
|
104
|
+
<% @attachments.each do |filename, attachment| %>
|
105
|
+
<%= link_to filename, attachment_url(attachment), download: filename %>
|
106
|
+
<% end %>
|
107
|
+
|
108
|
+
<% if @inline_attachments.any? %>
|
109
|
+
(Inline: <% @inline_attachments.each do |filename, attachment| %>
|
110
|
+
<%= link_to filename, attachment_url(attachment), download: filename %><% end %>)
|
107
111
|
<% end %>
|
108
112
|
</dd>
|
109
113
|
<% end %>
|
@@ -1,11 +1,12 @@
|
|
1
|
-
<% ruby_on_rails_logo_favicon_data_uri = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIwcHgiIGhlaWdodD0iMzIwcHgiIHZpZXdCb3g9IjAgMCAzMjAgMzIwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPHRpdGxlPkljb248L3RpdGxlPgogICAgPGcgaWQ9Ikljb24iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJSdWJ5LU9uLVJhaWxzLUxvZ28iIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUuMDAwMDAwLCAyMC4wMDAwMDApIiBmaWxsPSIjRDgxRTAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTIxLjkzMzA3MDYsMjg1IEwxNjguMTc1NDI2LDI4NSBDMTQ3Ljk0MDg1OCwxNTAuNjkxNzA2IDE5Ni44ODIzODMsNjYuMzE0MjkwMiAzMTUsMzEuODY3NzUzNiBDMzE1LDIxLjc4NTEyODQgMzE1LDMxLjg2Nzc1MzYgMzE1LDIxLjc4NTEyODQgQzE2MS4yNTI4MywyNC45MTE2Mjc4IDYzLjU2Mzg1MzMsMTEyLjY0OTkxOCAyMS45MzMwNzA2LDI4NSBaIiBpZD0iUGF0aCIvPgogICAgICAgICAgICA8cG9seWdvbiBpZD0iUGF0aCIgcG9pbnRzPSI0MC40MDgyNjQzIDE4NS45MTU3MSAxMi43MzI2NDk0IDE3NC40MDE2NjMgLTEuNDIxMDg1NDdlLTE0IDIwNS40NDI3MSAyOS41NzExOTY2IDIxNi42NDcxMTUiLz4KICAgICAgICAgICAgPHBvbHlnb24gaWQ9IlBhdGgiIHBvaW50cz0iMTgwLjQ3MzEwMiAyNjguMDczNjQzIDIwNC45MzYwMTYgMjc2LjQxMDk2NiAyMDQuOTM2MDE2IDI1NC41MDg2NzMgMTgwLjQ4OTY0NCAyNDQuMzM4MTA3Ii8+CiAgICAgICAgICAgIDxwb2x5Z29uIGlkPSJQYXRoIiBwb2ludHM9IjEwMC41ODk1MTkgOTcuMjI4NzYwNiA3Ni42ODQwMTU2IDc5LjE2ODcwMjEgNTUuMjQ0MDc5MSAxMDAuMTgzMTA1IDgxLjUxNDMyMzkgMTE3LjQzMzcyNSIvPgogICAgICAgICAgICA8cG9seWdvbiBpZD0iUGF0aCIgcG9pbnRzPSIxODQuNTc1Njc5IDE4NC44OTYyOTUgMjA3Ljk1MjAzIDIwMC4yNDY2MSAyMTEuNzI3NzI5IDE4MS4yMDUyNjYgMTg5Ljg2MzY1MyAxNjQuNjg3NDYiLz4KICAgICAgICAgICAgPHBvbHlnb24gaWQ9IlBhdGgiIHBvaW50cz0iMjYxLjczNDAxIDY1Ljg5NTk0NDYgMjY5LjM1NDIzNCA4Mi4zMjk1NCAyODUuMzE4MjU2IDcyLjcwNDg1OTYgMjc4LjMxMDEyOSA1Ni45ODI5ODk1Ii8+CiAgICAgICAgICAgIDxwb2x5Z29uIGlkPSJQYXRoIiBwb2ludHM9IjI2MS45MTM3IDE2LjE4NDU0NzMgMjU1LjU5ODQ3OSA3LjEwNTQyNzM2ZS0xNSAyMzIuNzk2MDIgMy40ODg5NjMyMyAyNDAuNDYzODczIDIwLjAyNTI3MjUiLz4KICAgICAgICAgICAgPHBvbHlnb24gaWQ9IlBhdGgiIHBvaW50cz0iMjExLjkzNDExOSAxMTEuNTgwNjc1IDIyNi43MjI1NDcgMTI3Ljc5MjYwMSAyMzguMDIzOTI1IDExMy45MDM3MTUgMjIzLjQ2ODM3NSA5Ni41NDY4Njg1Ii8+CiAgICAgICAgICAgIDxwb2x5Z29uIGlkPSJQYXRoIiBwb2ludHM9IjE3OS42ODY4NTggMzguNDk1MjIxOCAxNjQuNjQxOTMxIDIwLjAyNTI3MjUgMTM5Ljg0NzYyIDMyLjU1NTI5OTIgMTU2LjYwMTMyNCA1MC45MjE2NzUzIi8+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=" %>
|
2
1
|
<!DOCTYPE html>
|
3
2
|
<html>
|
4
3
|
<head>
|
5
4
|
<title>Ruby on Rails <%= Rails.version %></title>
|
6
5
|
<meta charset="utf-8">
|
7
6
|
<meta name="viewport" content="width=device-width">
|
8
|
-
|
7
|
+
|
8
|
+
<link rel="icon" href="/icon.png" type="image/png">
|
9
|
+
<link rel="apple-touch-icon" href="/icon.png" sizes="512x512">
|
9
10
|
|
10
11
|
<style type="text/css">
|
11
12
|
* {
|
@@ -84,6 +85,7 @@
|
|
84
85
|
|
85
86
|
<ul>
|
86
87
|
<li><strong>Rails version:</strong> <%= Rails.version %></li>
|
88
|
+
<li><strong>Rack version:</strong> <%= Rack.release %></li>
|
87
89
|
<li><strong>Ruby version:</strong> <%= RUBY_DESCRIPTION %></li>
|
88
90
|
</ul>
|
89
91
|
</body>
|
data/lib/rails/test_help.rb
CHANGED
@@ -7,19 +7,17 @@
|
|
7
7
|
abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
|
8
8
|
|
9
9
|
require "active_support/test_case"
|
10
|
-
require "action_controller"
|
11
|
-
require "action_controller/test_case"
|
12
|
-
require "action_dispatch/testing/integration"
|
13
10
|
require "rails/generators/test_case"
|
14
|
-
|
15
11
|
require "active_support/testing/autorun"
|
16
12
|
|
17
13
|
require "rails/testing/maintain_test_schema"
|
18
14
|
|
19
15
|
if defined?(ActiveRecord::Base)
|
16
|
+
require "active_record/testing/query_assertions"
|
20
17
|
ActiveSupport.on_load(:active_support_test_case) do
|
21
18
|
include ActiveRecord::TestDatabases
|
22
19
|
include ActiveRecord::TestFixtures
|
20
|
+
include ActiveRecord::Assertions::QueryAssertions
|
23
21
|
|
24
22
|
self.fixture_paths << "#{Rails.root}/test/fixtures/"
|
25
23
|
self.file_fixture_path = "#{Rails.root}/test/fixtures/files"
|
@@ -8,6 +8,13 @@ module Rails
|
|
8
8
|
class_attribute :app_root
|
9
9
|
class_attribute :executable, default: "bin/rails test"
|
10
10
|
|
11
|
+
def prerecord(test_class, test_name)
|
12
|
+
super
|
13
|
+
if options[:verbose]
|
14
|
+
io.print "%s#%s = " % [test_class.name, test_name]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
11
18
|
def record(result)
|
12
19
|
super
|
13
20
|
|
@@ -69,8 +76,7 @@ module Rails
|
|
69
76
|
end
|
70
77
|
|
71
78
|
def format_line(result)
|
72
|
-
|
73
|
-
"%s#%s = %.2f s = %s" % [klass, result.name, result.time, result.result_code]
|
79
|
+
"%.2f s = %s" % [result.time, result.result_code]
|
74
80
|
end
|
75
81
|
|
76
82
|
def format_rerun_snippet(result)
|
@@ -9,6 +9,15 @@ require "rails/test_unit/test_parser"
|
|
9
9
|
|
10
10
|
module Rails
|
11
11
|
module TestUnit
|
12
|
+
class InvalidTestError < StandardError
|
13
|
+
def initialize(path, suggestion)
|
14
|
+
super(<<~MESSAGE.squish)
|
15
|
+
Could not load test file: #{path}.
|
16
|
+
#{suggestion}
|
17
|
+
MESSAGE
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
12
21
|
class Runner
|
13
22
|
TEST_FOLDERS = [:models, :helpers, :channels, :controllers, :mailers, :integration, :jobs, :mailboxes]
|
14
23
|
PATH_ARGUMENT_PATTERN = %r"^(?!/.+/$)[.\w]*[/\\]"
|
@@ -48,7 +57,22 @@ module Rails
|
|
48
57
|
def load_tests(argv)
|
49
58
|
patterns = extract_filters(argv)
|
50
59
|
tests = list_tests(patterns)
|
51
|
-
tests.to_a.each
|
60
|
+
tests.to_a.each do |path|
|
61
|
+
abs_path = File.expand_path(path)
|
62
|
+
require abs_path
|
63
|
+
rescue LoadError => exception
|
64
|
+
if exception.path == abs_path
|
65
|
+
all_tests = list_tests([default_test_glob])
|
66
|
+
corrections = DidYouMean::SpellChecker.new(dictionary: all_tests).correct(path)
|
67
|
+
|
68
|
+
if corrections.empty?
|
69
|
+
raise exception
|
70
|
+
end
|
71
|
+
raise InvalidTestError.new(path, DidYouMean::Formatter.message_for(corrections))
|
72
|
+
else
|
73
|
+
raise
|
74
|
+
end
|
75
|
+
end
|
52
76
|
end
|
53
77
|
|
54
78
|
def compose_filter(runnable, filter)
|
@@ -87,7 +111,7 @@ module Rails
|
|
87
111
|
end
|
88
112
|
|
89
113
|
def default_test_exclude_glob
|
90
|
-
ENV["DEFAULT_TEST_EXCLUDE"] || "test/{system,dummy}/**/*_test.rb"
|
114
|
+
ENV["DEFAULT_TEST_EXCLUDE"] || "test/{system,dummy,fixtures}/**/*_test.rb"
|
91
115
|
end
|
92
116
|
|
93
117
|
def regexp_filter?(arg)
|
@@ -101,6 +125,7 @@ module Rails
|
|
101
125
|
def list_tests(patterns)
|
102
126
|
tests = Rake::FileList[patterns.any? ? patterns : default_test_glob]
|
103
127
|
tests.exclude(default_test_exclude_glob) if patterns.empty?
|
128
|
+
tests.exclude(%r{test/isolation/assets/node_modules})
|
104
129
|
tests
|
105
130
|
end
|
106
131
|
|
@@ -1,5 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
begin
|
4
|
+
require "prism"
|
5
|
+
rescue LoadError
|
6
|
+
# If Prism isn't available (because of using an older Ruby version) then we'll
|
7
|
+
# define a fallback parser using ripper.
|
8
|
+
end
|
9
|
+
|
10
|
+
if defined?(Prism)
|
11
|
+
module Rails
|
12
|
+
module TestUnit
|
13
|
+
# Parse a test file to extract the line ranges of all tests in both
|
14
|
+
# method-style (def test_foo) and declarative-style (test "foo" do)
|
15
|
+
module TestParser
|
16
|
+
@begins_to_ends = {}
|
17
|
+
# Helper to translate a method object into the path and line range where
|
18
|
+
# the method was defined.
|
19
|
+
def self.definition_for(method)
|
20
|
+
filepath, start_line = method.source_location
|
21
|
+
@begins_to_ends[filepath] ||= ranges(filepath)
|
22
|
+
return unless end_line = @begins_to_ends[filepath][start_line]
|
23
|
+
[filepath, start_line..end_line]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def self.ranges(filepath)
|
28
|
+
queue = [Prism.parse_file(filepath).value]
|
29
|
+
begins_to_ends = {}
|
30
|
+
while (node = queue.shift)
|
31
|
+
case node.type
|
32
|
+
when :def_node
|
33
|
+
begins_to_ends[node.location.start_line] = node.location.end_line
|
34
|
+
when :call_node
|
35
|
+
begins_to_ends[node.location.start_line] = node.location.end_line
|
36
|
+
end
|
37
|
+
|
38
|
+
queue.concat(node.compact_child_nodes)
|
39
|
+
end
|
40
|
+
begins_to_ends
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# If we have Prism, then we don't need to define the fallback parser using
|
47
|
+
# ripper.
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
3
51
|
require "ripper"
|
4
52
|
|
5
53
|
module Rails
|