All Files ( 15.5% covered at 2.4 hits/line )
138 files in total.
7213 relevant lines,
1118 lines covered and
6095 lines missed.
(
15.5%
)
# frozen_string_literal: true
require "active_support/core_ext/module/attribute_accessors"
require "rails/test_unit/reporter"
require "rails/test_unit/runner"
module Minitest
class SuppressedSummaryReporter < SummaryReporter
# Disable extra failure output after a run if output is inline.
def aggregated_results(*)
super unless options[:output_inline]
end
end
def self.plugin_rails_options(opts, options)
::Rails::TestUnit::Runner.attach_before_load_options(opts)
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 or error") do
options[:fail_fast] = true
end
opts.on("-c", "--[no-]color", "Enable color in the output") do |value|
options[:color] = value
end
options[:color] = true
options[:output_inline] = true
end
# Owes great inspiration to test runner trailblazers like RSpec,
# minitest-reporters, maxitest and others.
def self.plugin_rails_init(options)
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
# Suppress summary reports when outputting inline rerun snippets.
if reporter.reporters.reject! { |reporter| reporter.kind_of?(SummaryReporter) }
reporter << SuppressedSummaryReporter.new(options[:io], options)
end
# Replace progress reporter for colors.
if reporter.reporters.reject! { |reporter| reporter.kind_of?(ProgressReporter) }
reporter << ::Rails::TestUnitReporter.new(options[:io], options)
end
end
# Backwards compatibility with Rails 5.0 generated plugin test scripts
mattr_reader :run_via, default: {}
end
# frozen_string_literal: true
require "rails/ruby_version_check"
require "pathname"
require "active_support"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/object/blank"
require "rails/application"
require "rails/version"
require "rails/autoloaders"
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 Active Support 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::EnvironmentInquirer.new(ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development")
end
# Sets the Rails environment.
#
# Rails.env = "staging" # => "staging"
def env=(environment)
@_env = ActiveSupport::EnvironmentInquirer.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;
#
# Rails.groups assets: [:development, :test]
# # => [: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
def autoloaders
Autoloaders
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/RedundantBegin
- 17
require "rails"
%w(
active_record/railtie
active_storage/engine
action_controller/railtie
action_view/railtie
action_mailer/railtie
active_job/railtie
action_cable/engine
action_mailbox/engine
action_text/engine
rails/test_unit/railtie
sprockets/railtie
- 17
).each do |railtie|
- 187
begin
- 187
require railtie
rescue LoadError
end
end
# frozen_string_literal: true
require "sdoc"
require "active_support/core_ext/array/extract"
class RDoc::Generator::API < RDoc::Generator::SDoc # :nodoc:
RDoc::RDoc.add_generator self
def generate_class_tree_level(classes, visited = {})
# Only process core extensions on the first visit and remove
# Active Storage duplicated classes that are at the top level
# since they aren't nested under a definition of the `ActiveStorage` module.
if visited.empty?
classes = classes.reject { |klass| active_storage?(klass) }
core_exts = classes.extract! { |klass| core_extension?(klass) }
super.unshift([ "Core extensions", "", "", build_core_ext_subtree(core_exts, visited) ])
else
super
end
end
private
def build_core_ext_subtree(classes, visited)
classes.map do |klass|
[ klass.name, klass.document_self_or_methods ? klass.path : "", "",
generate_class_tree_level(klass.classes_and_modules, visited) ]
end
end
def core_extension?(klass)
klass.name != "ActiveSupport" && klass.in_files.any? { |file| file.absolute_name.include?("core_ext") }
end
def active_storage?(klass)
klass.name != "ActiveStorage" && klass.in_files.all? { |file| file.absolute_name.include?("active_storage") }
end
end
# frozen_string_literal: true
require "rdoc/task"
require "rails/api/generator"
module Rails
module API
class Task < RDoc::Task
RDOC_FILES = {
"activesupport" => {
include: %w(
README.rdoc
lib/active_support/**/*.rb
)
},
"activerecord" => {
include: %w(
README.rdoc
lib/active_record/**/*.rb
lib/arel.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
)
},
"actioncable" => {
include: %w(
README.md
lib/action_cable/**/*.rb
)
},
"activestorage" => {
include: %w(
README.md
app/**/active_storage/**/*.rb
lib/active_storage/**/*.rb
)
},
"actionmailbox" => {
include: %w(
README.md
app/**/action_mailbox/**/*.rb
lib/action_mailbox/**/*.rb
)
},
"actiontext" => {
include: %w(
README.md
app/**/action_text/**/*.rb
lib/action_text/**/*.rb
)
},
"railties" => {
include: %w(
README.rdoc
lib/**/*.rb
),
exclude: %w(
lib/rails/generators/**/templates/**/*.rb
lib/rails/test_unit/*
lib/rails/api/generator.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
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 configure_sdoc
self.title = "Ruby on Rails API"
self.rdoc_dir = api_dir
options << "-m" << api_main
options << "-e" << "UTF-8"
options << "-f" << "api"
options << "-T" << "rails"
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
# Only generate documentation for files that have been
# changed since the API was generated.
if Dir.exist?("doc/rdoc") && !ENV["ALL"]
last_generation = DateTime.rfc2822(File.open("doc/rdoc/created.rid", &:readline))
rdoc_files.keep_if do |file|
File.mtime(file).to_datetime > last_generation
end
# Nothing to do
exit(0) if rdoc_files.empty?
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 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
# frozen_string_literal: true
require "pathname"
require "rails/version"
module Rails
module AppLoader # :nodoc:
extend self
RUBY = Gem.ruby
EXECUTABLES = ["bin/rails", "script/rails"]
BUNDLER_WARNING = <<EOS
Beginning in Rails 4, Rails ships with a `rails` binstub at ./bin/rails that
should be used instead of the Bundler-generated `rails` binstub.
If you are seeing this message, your binstub at ./bin/rails was generated by
Bundler instead of Rails.
You might need to regenerate your `rails` binstub locally and add it to source
control:
rails app:update:bin # Bear in mind this generates other binstubs
# too that you may or may not want (like yarn)
If you already have Rails binstubs in source control, you might be
inadvertently overwriting them during deployment by using bundle install
with the --binstubs option.
If your application was created prior to Rails 4, here's how to upgrade:
bundle config --delete bin # Turn off Bundler's stub generator
rails app:update:bin # Use the new Rails 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 /(APP|ENGINE)_PATH/.match?(contents)
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) && 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
# frozen_string_literal: true
require "rails/generators"
require "rails/generators/rails/app/app_generator"
module Rails
class AppUpdater # :nodoc:
class << self
def invoke_from_app_generator(method)
app_generator.send(method)
end
def app_generator
@app_generator ||= begin
gen = Rails::Generators::AppGenerator.new ["rails"], generator_options, destination_root: Rails.root
File.exist?(Rails.root.join("config", "application.rb")) ? gen.send(:app_const) : gen.send(:valid_const?)
gen
end
end
private
def generator_options
options = { api: !!Rails.application.config.api_only, update: true }
options[:skip_javascript] = !File.exist?(Rails.root.join("bin", "yarn"))
options[:skip_active_record] = !defined?(ActiveRecord::Railtie)
options[:skip_active_storage] = !defined?(ActiveStorage::Engine) || !defined?(ActiveRecord::Railtie)
options[:skip_action_mailer] = !defined?(ActionMailer::Railtie)
options[:skip_action_cable] = !defined?(ActionCable::Engine)
options[:skip_sprockets] = !defined?(Sprockets::Railtie)
options[:skip_puma] = !defined?(Puma)
options[:skip_bootsnap] = !defined?(Bootsnap)
options[:skip_spring] = !defined?(Spring)
options
end
end
end
end
# frozen_string_literal: true
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 "active_support/encrypted_configuration"
require "active_support/deprecation"
require "active_support/hash_with_indifferent_access"
require "active_support/configuration_file"
require "rails/engine"
require "rails/secrets"
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 set up 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 them 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))
ActiveSupport.run_load_hooks(:before_configuration, base)
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, :reloader, :executor
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
@executor = Class.new(ActiveSupport::Executor)
@reloader = Class.new(ActiveSupport::Reloader)
@reloader.executor = @executor
# 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
@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 ||= ActiveSupport::CachingKeyGenerator.new(
ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
)
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.
#
# Examples:
#
# # 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/environments/production.rb
# Rails.application.configure do
# config.middleware.use ExceptionNotifier, config_for(:exception_notification)
# end
#
# # You can also store configurations in a shared section which will be
# # merged with the environment configuration
#
# # config/example.yml
# shared:
# foo:
# bar:
# baz: 1
#
# development:
# foo:
# bar:
# qux: 2
#
# # development environment
# Rails.application.config_for(:example)[:foo][:bar]
# # => { baz: 1, qux: 2 }
def config_for(name, env: Rails.env)
yaml = name.is_a?(Pathname) ? name : Pathname.new("#{paths["config"].existent.first}/#{name}.yml")
if yaml.exist?
require "erb"
all_configs = ActiveSupport::ConfigurationFile.parse(yaml, symbolize_names: true)
config, shared = all_configs[env.to_sym], all_configs[:shared]
if config.is_a?(Hash)
ActiveSupport::OrderedOptions.new.update(shared&.deep_merge(config) || config)
else
config || shared
end
else
raise "Could not load configuration. No such file - #{yaml}"
end
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
super.merge(
"action_dispatch.parameter_filter" => config.filter_parameters,
"action_dispatch.redirect_filter" => config.filter_redirect,
"action_dispatch.secret_key_base" => 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.authenticated_encrypted_cookie_salt" => config.action_dispatch.authenticated_encrypted_cookie_salt,
"action_dispatch.use_authenticated_cookie_encryption" => config.action_dispatch.use_authenticated_cookie_encryption,
"action_dispatch.encrypted_cookie_cipher" => config.action_dispatch.encrypted_cookie_cipher,
"action_dispatch.signed_cookie_digest" => config.action_dispatch.signed_cookie_digest,
"action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer,
"action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest,
"action_dispatch.cookies_rotations" => config.action_dispatch.cookies_rotations,
"action_dispatch.cookies_same_site_protection" => config.action_dispatch.cookies_same_site_protection,
"action_dispatch.use_cookies_with_metadata" => config.action_dispatch.use_cookies_with_metadata,
"action_dispatch.content_security_policy" => config.content_security_policy,
"action_dispatch.content_security_policy_report_only" => config.content_security_policy_report_only,
"action_dispatch.content_security_policy_nonce_generator" => config.content_security_policy_nonce_generator,
"action_dispatch.content_security_policy_nonce_directives" => config.content_security_policy_nonce_directives,
"action_dispatch.feature_policy" => config.feature_policy,
)
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|
File.file?(path) ? files << path.to_s : 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
attr_writer :config
# 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
files = config.paths["config/secrets"].existent
files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets
secrets.merge! Rails::Secrets.parse(files, env: Rails.env)
# Fallback to config.secret_key_base if secrets.secret_key_base isn't set
secrets.secret_key_base ||= config.secret_key_base
secrets
end
end
attr_writer :secrets
# The secret_key_base is used as the input secret to the application's key generator, which in turn
# is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
#
# In development and test, this is randomly generated and stored in a
# temporary file in <tt>tmp/development_secret.txt</tt>.
#
# In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
# then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
# the correct place to store it is in the encrypted credentials file.
def secret_key_base
if Rails.env.development? || Rails.env.test?
secrets.secret_key_base ||= generate_development_secret
else
validate_secret_key_base(
ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
)
end
end
# Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with
# the Rails master key, which is either taken from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading
# +config/master.key+.
# If specific credentials file exists for current environment, it takes precedence, thus for +production+
# environment look first for +config/credentials/production.yml.enc+ with master key taken
# from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading +config/credentials/production.key+.
# Default behavior can be overwritten by setting +config.credentials.content_path+ and +config.credentials.key_path+.
def credentials
@credentials ||= encrypted(config.credentials.content_path, key_path: config.credentials.key_path)
end
# Shorthand to decrypt any encrypted configurations or files.
#
# For any file added with <tt>rails encrypted:edit</tt> call +read+ to decrypt
# the file with the master key.
# The master key is either stored in +config/master.key+ or <tt>ENV["RAILS_MASTER_KEY"]</tt>.
#
# Rails.application.encrypted("config/mystery_man.txt.enc").read
# # => "We've met before, haven't we?"
#
# It's also possible to interpret encrypted YAML files with +config+.
#
# Rails.application.encrypted("config/credentials.yml.enc").config
# # => { next_guys_line: "I don't think so. Where was it you think we met?" }
#
# Any top-level configs are also accessible directly on the return value:
#
# Rails.application.encrypted("config/credentials.yml.enc").next_guys_line
# # => "I don't think so. Where was it you think we met?"
#
# The files or configs can also be encrypted with a custom key. To decrypt with
# a key in the +ENV+, use:
#
# Rails.application.encrypted("config/special_tokens.yml.enc", env_key: "SPECIAL_TOKENS")
#
# Or to decrypt with a file, that should be version control ignored, relative to +Rails.root+:
#
# Rails.application.encrypted("config/special_tokens.yml.enc", key_path: "config/special_tokens.key")
def encrypted(path, key_path: "config/master.key", env_key: "RAILS_MASTER_KEY")
ActiveSupport::EncryptedConfiguration.new(
config_path: Rails.root.join(path),
key_path: Rails.root.join(key_path),
env_key: env_key,
raise_if_missing_key: config.require_master_key
)
end
def to_app #:nodoc:
self
end
def helpers_paths #:nodoc:
config.helpers_paths
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
# Eager loads the application code.
def eager_load!
if Rails.autoloaders.zeitwerk_enabled?
Rails.autoloaders.each(&:eager_load)
else
super
end
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 = config.rake_eager_load }
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_base(secret_key_base)
if secret_key_base.is_a?(String) && secret_key_base.present?
secret_key_base
elsif secret_key_base
raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String`"
else
raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `bin/rails credentials:edit`"
end
end
private
def generate_development_secret
if secrets.secret_key_base.nil?
key_file = Rails.root.join("tmp/development_secret.txt")
if !File.exist?(key_file)
random_key = SecureRandom.hex(64)
FileUtils.mkdir_p(key_file.dirname)
File.binwrite(key_file, random_key)
end
secrets.secret_key_base = File.binread(key_file)
end
secrets.secret_key_base
end
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
# frozen_string_literal: true
require "fileutils"
require "active_support/notifications"
require "active_support/dependencies"
require "active_support/descendants_tracker"
require "rails/secrets"
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
logger = ActiveSupport::Logger.new(config.default_log_file)
logger.formatter = config.log_formatter
logger = ActiveSupport::TaggedLogging.new(logger)
logger
rescue StandardError
path = config.paths["log"].first
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
initializer :set_secrets_root, group: :all do
Rails::Secrets.root = root
end
end
end
end
# frozen_string_literal: true
- 17
require "ipaddr"
- 17
require "active_support/core_ext/kernel/reporting"
- 17
require "active_support/core_ext/symbol/starts_ends_with"
- 17
require "active_support/file_update_checker"
- 17
require "active_support/configuration_file"
- 17
require "rails/engine/configuration"
- 17
require "rails/source_annotation_extractor"
- 17
module Rails
- 17
class Application
- 17
class Configuration < ::Rails::Engine::Configuration
- 17
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, :hosts, :logger, :log_formatter, :log_tags,
:railties_order, :relative_url_root, :secret_key_base,
:ssl_options, :public_file_server,
:session_options, :time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x, :enable_dependency_loading,
:read_encrypted_secrets, :log_level, :content_security_policy_report_only,
:content_security_policy_nonce_generator, :content_security_policy_nonce_directives,
:require_master_key, :credentials, :disable_sandbox, :add_autoload_paths_to_load_path,
:rake_eager_load
- 17
attr_reader :encoding, :api_only, :loaded_config_version, :autoloader
- 17
def initialize(*)
- 17
super
- 17
self.encoding = Encoding::UTF_8
- 17
@allow_concurrency = nil
- 17
@consider_all_requests_local = false
- 17
@filter_parameters = []
- 17
@filter_redirect = []
- 17
@helpers_paths = []
- 17
@hosts = Array(([".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")] if Rails.env.development?))
- 17
@public_file_server = ActiveSupport::OrderedOptions.new
- 17
@public_file_server.enabled = true
- 17
@public_file_server.index_name = "index"
- 17
@force_ssl = false
- 17
@ssl_options = {}
- 17
@session_store = nil
- 17
@time_zone = "UTC"
- 17
@beginning_of_week = :monday
- 17
@log_level = :debug
- 17
@generators = app_generators
- 17
@cache_store = [ :file_store, "#{root}/tmp/cache/" ]
- 17
@railties_order = [:all]
- 17
@relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"]
- 17
@reload_classes_only_on_change = true
- 17
@file_watcher = ActiveSupport::FileUpdateChecker
- 17
@exceptions_app = nil
- 17
@autoflush_log = true
- 17
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
- 17
@eager_load = nil
- 17
@secret_key_base = nil
- 17
@api_only = false
- 17
@debug_exception_response_format = nil
- 17
@x = Custom.new
- 17
@enable_dependency_loading = false
- 17
@read_encrypted_secrets = false
- 17
@content_security_policy = nil
- 17
@content_security_policy_report_only = false
- 17
@content_security_policy_nonce_generator = nil
- 17
@content_security_policy_nonce_directives = nil
- 17
@require_master_key = false
- 17
@loaded_config_version = nil
- 17
@credentials = ActiveSupport::OrderedOptions.new
- 17
@credentials.content_path = default_credentials_content_path
- 17
@credentials.key_path = default_credentials_key_path
- 17
@autoloader = :classic
- 17
@disable_sandbox = false
- 17
@add_autoload_paths_to_load_path = true
- 17
@feature_policy = nil
- 17
@rake_eager_load = false
end
# Loads default configurations. See {the result of the method for each version}[https://guides.rubyonrails.org/configuring.html#results-of-config-load-defaults].
- 17
def load_defaults(target_version)
case target_version.to_s
when "5.0"
if respond_to?(:action_controller)
action_controller.per_form_csrf_tokens = true
action_controller.forgery_protection_origin_check = true
end
ActiveSupport.to_time_preserves_timezone = true
if respond_to?(:active_record)
active_record.belongs_to_required_by_default = true
end
self.ssl_options = { hsts: { subdomains: true } }
when "5.1"
load_defaults "5.0"
if respond_to?(:assets)
assets.unknown_asset_fallback = false
end
if respond_to?(:action_view)
action_view.form_with_generates_remote_forms = true
end
when "5.2"
load_defaults "5.1"
if respond_to?(:active_record)
active_record.cache_versioning = true
end
if respond_to?(:action_dispatch)
action_dispatch.use_authenticated_cookie_encryption = true
end
if respond_to?(:active_support)
active_support.use_authenticated_message_encryption = true
active_support.use_sha1_digests = true
end
if respond_to?(:action_controller)
action_controller.default_protect_from_forgery = true
end
if respond_to?(:action_view)
action_view.form_with_generates_ids = true
end
when "6.0"
load_defaults "5.2"
self.autoloader = :zeitwerk if RUBY_ENGINE == "ruby"
if respond_to?(:action_view)
action_view.default_enforce_utf8 = false
end
if respond_to?(:action_dispatch)
action_dispatch.use_cookies_with_metadata = true
action_dispatch.return_only_media_type_on_content_type = false
end
if respond_to?(:action_mailer)
action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
end
if respond_to?(:active_job)
active_job.return_false_on_aborted_enqueue = true
end
if respond_to?(:active_storage)
active_storage.queues.analysis = :active_storage_analysis
active_storage.queues.purge = :active_storage_purge
active_storage.replace_on_assign_to_many = true
end
if respond_to?(:active_record)
active_record.collection_cache_versioning = true
end
when "6.1"
load_defaults "6.0"
if respond_to?(:active_record)
active_record.has_many_inversing = true
end
if respond_to?(:active_storage)
active_storage.track_variants = true
end
if respond_to?(:active_job)
active_job.retry_jitter = 0.15
active_job.skip_after_callbacks_if_terminated = true
end
if respond_to?(:action_dispatch)
action_dispatch.cookies_same_site_protection = :lax
action_dispatch.ssl_default_redirect_status = 308
end
if respond_to?(:action_controller)
action_controller.urlsafe_csrf_tokens = true
end
ActiveSupport.utc_to_local_returns_utc_offset_times = true
else
raise "Unknown version #{target_version.to_s.inspect}"
end
@loaded_config_version = target_version
end
- 17
def encoding=(value)
- 17
@encoding = value
- 17
silence_warnings do
- 17
Encoding.default_external = value
- 17
Encoding.default_internal = value
end
end
- 17
def api_only=(value)
@api_only = value
generators.api_only = value
@debug_exception_response_format ||= :api
end
- 17
def debug_exception_response_format
@debug_exception_response_format || :default
end
- 17
attr_writer :debug_exception_response_format
- 17
def paths
- 33
@paths ||= begin
- 17
paths = super
- 17
paths.add "config/database", with: "config/database.yml"
- 17
paths.add "config/secrets", with: "config", glob: "secrets.yml{,.enc}"
- 17
paths.add "config/environment", with: "config/environment.rb"
- 17
paths.add "lib/templates"
- 17
paths.add "log", with: "log/#{Rails.env}.log"
- 17
paths.add "public"
- 17
paths.add "public/javascripts"
- 17
paths.add "public/stylesheets"
- 17
paths.add "tmp"
- 17
paths
end
end
# Load the database YAML without evaluating ERB. This allows us to
# create the rake tasks for multiple databases without filling in the
# configuration values or loading the environment. Do not use this
# method.
#
# This uses a DummyERB custom compiler so YAML can ignore the ERB
# tags and load the database.yml for the rake tasks.
- 17
def load_database_yaml # :nodoc:
if path = paths["config/database"].existent.first
require "rails/application/dummy_erb_compiler"
yaml = Pathname.new(path)
erb = DummyERB.new(yaml.read)
YAML.load(erb.result) || {}
else
{}
end
end
# Loads and returns the entire raw configuration of database from
# values stored in <tt>config/database.yml</tt>.
- 17
def database_configuration
path = paths["config/database"].existent.first
yaml = Pathname.new(path) if path
config = if yaml&.exist?
loaded_yaml = ActiveSupport::ConfigurationFile.parse(yaml)
if (shared = loaded_yaml.delete("shared"))
loaded_yaml.each do |_k, values|
values.reverse_merge!(shared)
end
end
Hash.new(shared).merge(loaded_yaml)
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 => e
raise e, "Cannot load database configuration:\n#{e.message}", e.backtrace
end
- 17
def colorize_logging
ActiveSupport::LogSubscriber.colorize_logging
end
- 17
def colorize_logging=(val)
ActiveSupport::LogSubscriber.colorize_logging = val
generators.colorize_logging = val
end
- 17
def session_store(new_session_store = nil, **options)
if new_session_store
if new_session_store == :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
end
@session_store = new_session_store
@session_options = options || {}
else
case @session_store
when :disabled
nil
when :active_record_store
ActionDispatch::Session::ActiveRecordStore
when Symbol
ActionDispatch::Session.const_get(@session_store.to_s.camelize)
else
@session_store
end
end
end
- 17
def session_store? #:nodoc:
@session_store
end
- 17
def annotations
Rails::SourceAnnotationExtractor::Annotation
end
- 17
def content_security_policy(&block)
if block_given?
@content_security_policy = ActionDispatch::ContentSecurityPolicy.new(&block)
else
@content_security_policy
end
end
- 17
def feature_policy(&block)
if block_given?
@feature_policy = ActionDispatch::FeaturePolicy.new(&block)
else
@feature_policy
end
end
- 17
def autoloader=(autoloader)
case autoloader
when :classic
@autoloader = autoloader
when :zeitwerk
require "zeitwerk"
@autoloader = autoloader
else
raise ArgumentError, "config.autoloader may be :classic or :zeitwerk, got #{autoloader.inspect} instead"
end
end
- 17
def default_log_file
path = 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 = autoflush_log # if true make sure every write flushes
f
end
- 17
class Custom #:nodoc:
- 17
def initialize
- 17
@configurations = Hash.new
end
- 17
def method_missing(method, *args)
if method.end_with?("=")
@configurations[:"#{method[0..-2]}"] = args.first
else
@configurations.fetch(method) {
@configurations[method] = ActiveSupport::OrderedOptions.new
}
end
end
- 17
def respond_to_missing?(symbol, *)
true
end
end
- 17
private
- 17
def default_credentials_content_path
- 17
if credentials_available_for_current_env?
root.join("config", "credentials", "#{Rails.env}.yml.enc")
else
- 17
root.join("config", "credentials.yml.enc")
end
end
- 17
def default_credentials_key_path
- 17
if credentials_available_for_current_env?
root.join("config", "credentials", "#{Rails.env}.key")
else
- 17
root.join("config", "master.key")
end
end
- 17
def credentials_available_for_current_env?
- 34
File.exist?(root.join("config", "credentials", "#{Rails.env}.yml.enc"))
end
end
end
end
# frozen_string_literal: true
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 do |middleware|
middleware.use ::ActionDispatch::HostAuthorization, config.hosts, config.action_dispatch.hosts_response_app
if config.force_ssl
middleware.use ::ActionDispatch::SSL, **config.ssl_options,
ssl_default_redirect_status: config.action_dispatch.ssl_default_redirect_status
end
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
if config.public_file_server.enabled
headers = config.public_file_server.headers || {}
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
end
middleware.use ::ActionDispatch::Executor, app.executor
middleware.use ::Rack::Runtime
middleware.use ::Rack::MethodOverride unless config.api_only
middleware.use ::ActionDispatch::RequestId
middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
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::ActionableExceptions
unless config.cache_classes
middleware.use ::ActionDispatch::Reloader, app.reloader
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.ssl_options.fetch(:secure_cookies, true) && !config.session_options.key?(:secure)
config.session_options[:secure] = true
end
middleware.use config.session_store, config.session_options
middleware.use ::ActionDispatch::Flash
end
unless config.api_only
middleware.use ::ActionDispatch::ContentSecurityPolicy::Middleware
middleware.use ::ActionDispatch::FeaturePolicy::Middleware
end
middleware.use ::Rack::Head
middleware.use ::Rack::ConditionalGet
middleware.use ::Rack::ETag, "no-cache"
middleware.use ::Rack::TempfileReaper unless config.api_only
end
end
private
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
# frozen_string_literal: true
# These classes are used to strip out the ERB configuration
# values so we can evaluate the database.yml without evaluating
# the ERB values.
class DummyERB < ERB # :nodoc:
def make_compiler(trim_mode)
DummyCompiler.new trim_mode
end
end
class DummyCompiler < ERB::Compiler # :nodoc:
def compile_content(stag, out)
if stag == "<%="
out.push "_erbout << ''"
end
end
end
# frozen_string_literal: true
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/array/conversions"
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
# This will become an error if/when we remove classic mode. The plan is
# autoloaders won't be configured up to this point in the finisher, so
# constants just won't be found, raising regular NameError exceptions.
initializer :warn_if_autoloaded, before: :let_zeitwerk_take_over do
next if config.cache_classes
next if ActiveSupport::Dependencies.autoloaded_constants.empty?
autoloaded = ActiveSupport::Dependencies.autoloaded_constants
constants = "constant".pluralize(autoloaded.size)
enum = autoloaded.to_sentence
have = autoloaded.size == 1 ? "has" : "have"
these = autoloaded.size == 1 ? "This" : "These"
example = autoloaded.first
example_klass = example.constantize.class
if config.autoloader == :zeitwerk
ActiveSupport::DescendantsTracker.clear
ActiveSupport::Dependencies.clear
unload_message = "#{these} autoloaded #{constants} #{have} been unloaded."
else
unload_message = "`config.autoloader` is set to `#{config.autoloader}`. #{these} autoloaded #{constants} would have been unloaded if `config.autoloader` had been set to `:zeitwerk`."
end
ActiveSupport::Deprecation.warn(<<~WARNING)
Initialization autoloaded the #{constants} #{enum}.
Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.
Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload #{example}, for example,
the expected changes won't be reflected in that stale #{example_klass} object.
#{unload_message}
In order to autoload safely at boot time, please wrap your code in a reloader
callback this way:
Rails.application.reloader.to_prepare do
# Autoload classes and modules needed at boot time here.
end
That block runs when the application boots, and every time there is a reload.
For historical reasons, it may run twice, so it has to be idempotent.
Check the "Autoloading and Reloading Constants" guide to learn more about how
Rails autoloads and reloads.
WARNING
end
initializer :let_zeitwerk_take_over do
if config.autoloader == :zeitwerk
require "active_support/dependencies/zeitwerk_integration"
ActiveSupport::Dependencies::ZeitwerkIntegration.take_over(enable_reloading: !config.cache_classes)
end
end
initializer :add_builtin_route do |app|
if Rails.env.development?
app.routes.prepend do
get "/rails/info/properties" => "rails/info#properties", internal: true
get "/rails/info/routes" => "rails/info#routes", internal: true
get "/rails/info" => "rails/info#index", internal: true
end
app.routes.append do
get "/" => "rails/welcome#index", internal: true
end
end
end
# Setup default session store if not already set in config/application.rb
initializer :setup_default_session_store, before: :build_middleware_stack do |app|
unless app.config.session_store?
app_name = app.class.name ? app.railtie_name.chomp("_application") : ""
app.config.session_store :cookie_store, key: "_#{app_name}_session"
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 |app|
config.to_prepare_blocks.each do |block|
app.reloader.to_prepare(&block)
end
end
# This needs to happen before eager load so it happens
# in exactly the same point regardless of config.eager_load
initializer :run_prepare_callbacks do |app|
app.reloader.prepare!
end
initializer :eager_load! do
if config.eager_load
ActiveSupport.run_load_hooks(:before_eager_load, self)
# Checks defined?(Zeitwerk) instead of zeitwerk_enabled? because we
# want to eager load any dependency managed by Zeitwerk regardless of
# the autoloading mode of the application.
Zeitwerk::Loader.eager_load_all if defined?(Zeitwerk)
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
class MutexHook
def initialize(mutex = Mutex.new)
@mutex = mutex
end
def run
@mutex.lock
end
def complete(_state)
@mutex.unlock
end
end
module InterlockHook
def self.run
ActiveSupport::Dependencies.interlock.start_running
end
def self.complete(_state)
ActiveSupport::Dependencies.interlock.done_running
end
end
initializer :configure_executor_for_concurrency do |app|
if config.allow_concurrency == false
# User has explicitly opted out of concurrent request
# handling: presumably their code is not threadsafe
app.executor.register_hook(MutexHook.new, outer: true)
elsif config.allow_concurrency == :unsafe
# Do nothing, even if we know this is dangerous. This is the
# historical behavior 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
app.executor.register_hook(InterlockHook, outer: true)
end
end
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 |app|
reloader = routes_reloader
reloader.eager_load = app.config.eager_load
reloader.execute
reloaders << reloader
app.reloader.to_run 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.
require_unload_lock!
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 |app|
callback = lambda do
ActiveSupport::DescendantsTracker.clear
ActiveSupport::Dependencies.clear
end
if config.cache_classes
app.reloader.check = lambda { false }
elsif config.reload_classes_only_on_change
app.reloader.check = lambda do
app.reloaders.map(&:updated?).any?
end
else
app.reloader.check = lambda { true }
end
if config.cache_classes
# No reloader
elsif config.reload_classes_only_on_change
reloader = config.file_watcher.new(*watchable_args, &callback)
reloaders << reloader
# Prepend this callback to have autoloaded constants cleared before
# any other possible reloading, in case they need to autoload fresh
# constants.
app.reloader.to_run(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.
class_unload! do
reloader.execute
end
end
else
app.reloader.to_complete do
class_unload!(&callback)
end
end
end
# Disable dependency loading during request cycle
initializer :disable_dependency_loading do
if config.eager_load && config.cache_classes && !config.enable_dependency_loading
ActiveSupport::Dependencies.unhook!
end
end
end
end
end
# frozen_string_literal: true
require "active_support/core_ext/module/delegation"
module Rails
class Application
class RoutesReloader
attr_reader :route_sets, :paths, :external_routes
attr_accessor :eager_load
delegate :execute_if_updated, :execute, :updated?, to: :updater
def initialize
@paths = []
@route_sets = []
@external_routes = []
@eager_load = false
end
def reload!
clear!
load_paths
finalize!
route_sets.each(&:eager_load!) if eager_load
ensure
revert
end
private
def updater
@updater ||= begin
dirs = @external_routes.each_with_object({}) do |dir, hash|
hash[dir.to_s] = %w(rb)
end
ActiveSupport::FileUpdateChecker.new(paths, dirs) { reload! }
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
# frozen_string_literal: true
class Rails::ApplicationController < ActionController::Base # :nodoc:
self.view_paths = File.expand_path("templates", __dir__)
layout "application"
before_action :disable_content_security_policy_nonce!
content_security_policy do |policy|
policy.script_src :unsafe_inline
policy.style_src :unsafe_inline
end
private
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
def disable_content_security_policy_nonce!
request.content_security_policy_nonce_generator = nil
end
end
# frozen_string_literal: true
require "active_support/dependencies/zeitwerk_integration"
module Rails
module Autoloaders # :nodoc:
class << self
include Enumerable
def main
if zeitwerk_enabled?
@main ||= Zeitwerk::Loader.new.tap do |loader|
loader.tag = "rails.main"
loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
end
end
end
def once
if zeitwerk_enabled?
@once ||= Zeitwerk::Loader.new.tap do |loader|
loader.tag = "rails.once"
loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
end
end
end
def each
if zeitwerk_enabled?
yield main
yield once
end
end
def logger=(logger)
each { |loader| loader.logger = logger }
end
def log!
each(&:log!)
end
def zeitwerk_enabled?
Rails.configuration.autoloader == :zeitwerk
end
end
end
end
# frozen_string_literal: true
require "active_support/backtrace_cleaner"
require "active_support/core_ext/string/access"
module Rails
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
APP_DIRS_PATTERN = /\A(?:\.\/)?(?:app|config|lib|test|\(\w*\))/
RENDER_TEMPLATE_PATTERN = /:in `.*_\w+_{2,3}\d+_\d+'/
def initialize
super
@root = "#{Rails.root}/"
add_filter do |line|
line.start_with?(@root) ? line.from(@root.size) : line
end
add_filter do |line|
if RENDER_TEMPLATE_PATTERN.match?(line)
line.sub(RENDER_TEMPLATE_PATTERN, "")
else
line
end
end
add_silencer { |line| !APP_DIRS_PATTERN.match?(line) }
end
end
end
# frozen_string_literal: true
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) }
require "rails/command"
if ARGV.first == "plugin"
ARGV.shift
Rails::Command.invoke :plugin, ARGV
else
Rails::Command.invoke :application, ARGV
end
# frozen_string_literal: true
require "rails/code_statistics_calculator"
require "active_support/core_ext/enumerable"
class CodeStatistics #:nodoc:
TEST_TYPES = ["Controller tests",
"Helper tests",
"Model tests",
"Mailer tests",
"Mailbox tests",
"Channel tests",
"Job tests",
"Integration tests",
"System tests"]
HEADERS = { lines: " Lines", code_lines: " LOC", classes: "Classes", methods: "Methods" }
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|ts|coffee|rake)$/)
stats = CodeStatisticsCalculator.new
Dir.foreach(directory) do |file_name|
path = "#{directory}/#{file_name}"
if File.directory?(path) && !file_name.start_with?(".")
stats.add(calculate_directory_statistics(path, pattern))
elsif file_name&.match?(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 width_for(label)
[@statistics.values.sum { |s| s.send(label) }.to_s.size, HEADERS[label].length].max
end
def print_header
print_splitter
print "| Name "
HEADERS.each do |k, v|
print " | #{v.rjust(width_for(k))}"
end
puts " | M/C | LOC/M |"
print_splitter
end
def print_splitter
print "+----------------------"
HEADERS.each_key do |k|
print "+#{'-' * (width_for(k) + 2)}"
end
puts "+-----+-------+"
end
def print_line(name, statistics)
m_over_c = (statistics.methods / statistics.classes) rescue 0
loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue 0
print "| #{name.ljust(20)} "
HEADERS.each_key do |k|
print "| #{statistics.send(k).to_s.rjust(width_for(k))} "
end
puts "| #{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
# frozen_string_literal: true
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|
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] && patterns[:end_block_comment].match?(line)
comment_started = false
end
next
else
if patterns[:begin_block_comment] && patterns[:begin_block_comment].match?(line)
comment_started = true
next
end
end
@classes += 1 if patterns[:class] && patterns[:class].match?(line)
@methods += 1 if patterns[:method] && patterns[:method].match?(line)
if !line.match?(/^\s*$/) && (patterns[:line_comment].nil? || !line.match?(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).delete_prefix(".").downcase.to_sym
end
end
end
# frozen_string_literal: true
- 16
require "active_support"
- 16
require "active_support/core_ext/enumerable"
- 16
require "active_support/core_ext/object/blank"
- 16
require "thor"
- 16
module Rails
- 16
module Command
- 16
extend ActiveSupport::Autoload
- 16
autoload :Spellchecker
- 16
autoload :Behavior
- 16
autoload :Base
- 16
include Behavior
- 16
HELP_MAPPINGS = %w(-h -? --help)
- 16
class << self
- 16
def hidden_commands # :nodoc:
@hidden_commands ||= []
end
- 16
def environment # :nodoc:
ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development"
end
# Receives a namespace, arguments and the behavior to invoke the command.
- 16
def invoke(full_namespace, args = [], **config)
namespace = full_namespace = full_namespace.to_s
if char = namespace =~ /:(\w+)$/
command_name, namespace = $1, namespace.slice(0, char)
else
command_name = namespace
end
command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)
command_name, namespace = "version", "version" if %w( -v --version ).include?(command_name)
# isolate ARGV to ensure that commands depend only on the args they are given
args = args.dup # args might *be* ARGV so dup before clearing
old_argv = ARGV.dup
ARGV.clear
command = find_by_namespace(namespace, command_name)
if command && command.all_commands[command_name]
command.perform(command_name, args, config)
else
find_by_namespace("rake").perform(full_namespace, args, config)
end
ensure
ARGV.replace(old_argv)
end
# Rails finds namespaces similar to Thor, it only adds one rule:
#
# Command names must end with "_command.rb". This is required because Rails
# looks in load paths and loads the command just before it's going to be used.
#
# find_by_namespace :webrat, :rails, :integration
#
# Will search for the following commands:
#
# "rails:webrat", "webrat:integration", "webrat"
#
# Notice that "rails:commands:webrat" could be loaded as well, what
# Rails looks for is the first and last parts of the namespace.
- 16
def find_by_namespace(namespace, command_name = nil) # :nodoc:
lookups = [ namespace ]
lookups << "#{namespace}:#{command_name}" if command_name
lookups.concat lookups.map { |lookup| "rails:#{lookup}" }
lookup(lookups)
namespaces = subclasses.index_by(&:namespace)
namespaces[(lookups & namespaces.keys).first]
end
# Returns the root of the Rails engine or app running the command.
- 16
def root
if defined?(ENGINE_ROOT)
Pathname.new(ENGINE_ROOT)
elsif defined?(APP_PATH)
Pathname.new(File.expand_path("../..", APP_PATH))
end
end
- 16
def print_commands # :nodoc:
commands.each { |command| puts(" #{command}") }
end
- 16
private
- 16
COMMANDS_IN_USAGE = %w(generate console server test test:system dbconsole new)
- 16
private_constant :COMMANDS_IN_USAGE
- 16
def commands
lookup!
visible_commands = (subclasses - hidden_commands).flat_map(&:printing_commands)
(visible_commands - COMMANDS_IN_USAGE).sort
end
- 16
def command_type # :doc:
@command_type ||= "command"
end
- 16
def lookup_paths # :doc:
@lookup_paths ||= %w( rails/commands commands )
end
- 16
def file_lookup_paths # :doc:
@file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_command.rb" ]
end
end
end
end
# frozen_string_literal: true
module Rails
module Command
module Actions
# Change to the application's path if there is no <tt>config.ru</tt> file in current directory.
# This allows us to run <tt>rails server</tt> from other directories, but still get
# the main <tt>config.ru</tt> and properly set the <tt>tmp</tt> 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_application!
require_environment!
end
def require_application!
require ENGINE_PATH if defined?(ENGINE_PATH)
if defined?(APP_PATH)
require APP_PATH
end
end
def require_environment!
if defined?(APP_PATH)
Rails.application.require_environment!
end
end
if defined?(ENGINE_PATH)
def load_tasks
Rake.application.init("rails")
Rake.application.load_rakefile
end
def load_generators
engine = ::Rails::Engine.find(ENGINE_ROOT)
Rails::Generators.namespace = engine.railtie_namespace
engine.load_generators
end
else
def load_tasks
Rails.application.load_tasks
end
def load_generators
Rails.application.load_generators
end
end
end
end
end
# frozen_string_literal: true
require "thor"
require "erb"
require "active_support/core_ext/string/filters"
require "active_support/core_ext/string/inflections"
require "rails/command/actions"
module Rails
module Command
class Base < Thor
class Error < Thor::Error # :nodoc:
end
include Actions
class << self
def exit_on_failure? # :nodoc:
false
end
# Returns true when the app is a Rails engine.
def engine?
defined?(ENGINE_ROOT)
end
# Tries to get the description from a USAGE file one folder above the command
# root.
def desc(usage = nil, description = nil, options = {})
if usage
super
else
@desc ||= ERB.new(File.read(usage_path)).result(binding) if usage_path
end
end
# Convenience method to get the namespace from the class name. It's the
# same as Thor default except that the Command at the end of the class
# is removed.
def namespace(name = nil)
if name
super
else
@namespace ||= super.chomp("_command").sub(/:command:/, ":")
end
end
# Convenience method to hide this command from the available ones when
# running rails command.
def hide_command!
Rails::Command.hidden_commands << self
end
def inherited(base) #:nodoc:
super
if base.name && !base.name.end_with?("Base")
Rails::Command.subclasses << base
end
end
def perform(command, args, config) # :nodoc:
if Rails::Command::HELP_MAPPINGS.include?(args.first)
command, args = "help", []
end
dispatch(command, args.dup, nil, config)
end
def printing_commands
namespaced_commands
end
def executable
"rails #{command_name}"
end
# Use Rails' default banner.
def banner(*)
"#{executable} #{arguments.map(&:usage).join(' ')} [options]".squish
end
# Sets the base_name taking into account the current class namespace.
#
# Rails::Command::TestCommand.base_name # => 'rails'
def base_name
@base_name ||= begin
if base = name.to_s.split("::").first
base.underscore
end
end
end
# Return command name without namespaces.
#
# Rails::Command::TestCommand.command_name # => 'test'
def command_name
@command_name ||= begin
if command = name.to_s.split("::").last
command.chomp!("Command")
command.underscore
end
end
end
# Path to lookup a USAGE description in a file.
def usage_path
if default_command_root
path = File.join(default_command_root, "USAGE")
path if File.exist?(path)
end
end
# Default file root to place extra files a command might need, placed
# one folder above the command file.
#
# For a Rails::Command::TestCommand placed in <tt>rails/command/test_command.rb</tt>
# would return <tt>rails/test</tt>.
def default_command_root
path = File.expand_path(relative_command_path, __dir__)
path if File.exist?(path)
end
private
# Allow the command method to be called perform.
def create_command(meth)
if meth == "perform"
alias_method command_name, meth
else
# Prevent exception about command without usage.
# Some commands define their documentation differently.
@usage ||= ""
@desc ||= ""
super
end
end
def command_root_namespace
(namespace.split(":") - %w(rails)).join(":")
end
def relative_command_path
File.join("../commands", *command_root_namespace.split(":"))
end
def namespaced_commands
commands.keys.map do |key|
if command_root_namespace.match?(/(\A|\:)#{key}\z/)
command_root_namespace
else
"#{command_root_namespace}:#{key}"
end
end
end
end
def help
if command_name = self.class.command_name
self.class.command_help(shell, command_name)
else
super
end
end
end
end
end
# frozen_string_literal: true
- 16
require "active_support"
- 16
module Rails
- 16
module Command
- 16
module Behavior #:nodoc:
- 16
extend ActiveSupport::Concern
- 16
class_methods do
# Remove the color from output.
- 16
def no_color!
- 16
Thor::Base.shell = Thor::Shell::Basic
end
# Track all command subclasses.
- 16
def subclasses
- 23
@subclasses ||= []
end
- 16
private
# Prints a list of generators.
- 16
def print_list(base, namespaces)
return if namespaces.empty?
puts "#{base.camelize}:"
namespaces.each do |namespace|
puts(" #{namespace}")
end
puts
end
# Receives namespaces in an array and tries to find matching generators
# in the load path.
- 16
def lookup(namespaces)
paths = namespaces_to_paths(namespaces)
paths.each do |raw_path|
lookup_paths.each do |base|
path = "#{base}/#{raw_path}_#{command_type}"
begin
require path
return
rescue LoadError => e
raise unless /#{Regexp.escape(path)}$/.match?(e.message)
rescue Exception => e
warn "[WARNING] Could not load #{command_type} #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
end
end
end
end
# This will try to load any command in the load path to show in help.
- 16
def lookup!
$LOAD_PATH.each do |base|
Dir[File.join(base, *file_lookup_paths)].each do |path|
path = path.delete_prefix("#{base}/")
require path
rescue Exception
# No problem
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".
- 16
def namespaces_to_paths(namespaces)
paths = []
namespaces.each do |namespace|
pieces = namespace.split(":")
path = pieces.join("/")
paths << "#{path}/#{pieces.last}"
paths << path
end
paths.uniq!
paths
end
end
end
end
end
# frozen_string_literal: true
require "active_support"
require "active_support/core_ext/class/attribute"
module Rails
module Command
module EnvironmentArgument #:nodoc:
extend ActiveSupport::Concern
included do
no_commands do
class_attribute :environment_desc, default: "Specifies the environment to run this #{self.command_name} under (test/development/production)."
end
class_option :environment, aliases: "-e", type: :string, desc: environment_desc
end
private
def extract_environment_option_from_argument(default_environment: Rails::Command.environment)
if options[:environment]
self.options = options.merge(environment: acceptable_environment(options[:environment]))
else
self.options = options.merge(environment: default_environment)
end
end
def acceptable_environment(env = nil)
if available_environments.include? env
env
else
%w( production development test ).detect { |e| /^#{env}/.match?(e) } || env
end
end
def available_environments
Dir["config/environments/*.rb"].map { |fname| File.basename(fname, ".*") }
end
end
end
end
# frozen_string_literal: true
require "active_support/encrypted_file"
module Rails
module Command
module Helpers
module Editor
private
def ensure_editor_available(command:)
if ENV["EDITOR"].to_s.empty?
say "No $EDITOR to open file in. Assign one like this:"
say ""
say %(EDITOR="mate --wait" #{command})
say ""
say "For editors that fork and exit immediately, it's important to pass a wait flag,"
say "otherwise the credentials will be saved immediately with no chance to edit."
false
else
true
end
end
def catch_editing_exceptions
yield
rescue Interrupt
say "Aborted changing file: nothing saved."
rescue ActiveSupport::EncryptedFile::MissingKeyError => error
say error.message
end
end
end
end
end
# frozen_string_literal: true
module Rails
module Command
module Spellchecker # :nodoc:
class << self
def suggest(word, from:)
if defined?(DidYouMean::SpellChecker)
DidYouMean::SpellChecker.new(dictionary: from.map(&:to_s)).correct(word).first
else
from.sort_by { |w| levenshtein_distance(word, w) }.first
end
end
private
# This code is based directly on the Text gem implementation.
# Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
#
# Returns a value representing the "cost" of transforming str1 into str2.
def levenshtein_distance(str1, str2) # :doc:
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
end
end
end
end
# frozen_string_literal: true
require "rails/command"
aliases = {
"g" => "generate",
"d" => "destroy",
"c" => "console",
"s" => "server",
"db" => "dbconsole",
"r" => "runner",
"t" => "test"
}
command = ARGV.shift
command = aliases[command] || command
Rails::Command.invoke command, ARGV
# frozen_string_literal: true
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
module Command
class ApplicationCommand < Base # :nodoc:
hide_command!
def help
perform # Punt help output to the generator.
end
def perform(*args)
Rails::Generators::AppGenerator.start \
Rails::Generators::ARGVScrubber.new(args).prepare!
end
end
end
end
# frozen_string_literal: true
require "irb"
require "irb/completion"
require "rails/command/environment_argument"
module Rails
class Console
module BacktraceCleaner
def filter_backtrace(bt)
if result = super
Rails.backtrace_cleaner.filter([result]).first
end
end
end
def self.start(*args)
new(*args).start
end
attr_reader :options, :app, :console
def initialize(app, options = {})
@app = app
@options = options
app.sandbox = sandbox?
if sandbox? && app.config.disable_sandbox
puts "Error: Unable to start console in sandbox mode as sandbox mode is disabled (config.disable_sandbox is true)."
exit 1
end
app.load_console
@console = app.config.console || IRB
if @console == IRB
IRB::WorkSpace.prepend(BacktraceCleaner)
end
end
def sandbox?
options[:sandbox]
end
def environment
options[:environment]
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
module Command
class ConsoleCommand < Base # :nodoc:
include EnvironmentArgument
class_option :sandbox, aliases: "-s", type: :boolean, default: false,
desc: "Rollback database modifications on exit."
def initialize(args = [], local_options = {}, config = {})
console_options = []
# For the same behavior as OptionParser, leave only options after "--" in ARGV.
termination = local_options.find_index("--")
if termination
console_options = local_options[termination + 1..-1]
local_options = local_options[0...termination]
end
ARGV.replace(console_options)
super(args, local_options, config)
end
def perform
extract_environment_option_from_argument
# RAILS_ENV needs to be set before config/application is required.
ENV["RAILS_ENV"] = options[:environment]
require_application_and_environment!
Rails::Console.start(Rails.application, options)
end
end
end
end
# frozen_string_literal: true
require "pathname"
require "active_support"
require "rails/command/helpers/editor"
require "rails/command/environment_argument"
module Rails
module Command
class CredentialsCommand < Rails::Command::Base # :nodoc:
include Helpers::Editor
include EnvironmentArgument
require_relative "credentials_command/diffing"
include Diffing
self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
no_commands do
def help
say "Usage:\n #{self.class.banner}"
say ""
say self.class.desc
end
end
def edit
extract_environment_option_from_argument(default_environment: nil)
require_application!
ensure_editor_available(command: "bin/rails credentials:edit") || (return)
ensure_encryption_key_has_been_added if credentials.key.nil?
ensure_credentials_have_been_added
ensure_rails_credentials_driver_is_set
catch_editing_exceptions do
change_credentials_in_system_editor
end
say "File encrypted and saved."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
end
def show
extract_environment_option_from_argument(default_environment: nil)
require_application!
say credentials.read.presence || missing_credentials_message
end
option :enroll, type: :boolean, default: false,
desc: "Enrolls project in credential file diffing with `git diff`"
def diff(content_path = nil)
if @content_path = content_path
extract_environment_option_from_argument(default_environment: extract_environment_from_path(content_path))
require_application!
say credentials.read.presence || credentials.content_path.read
else
require_application!
enroll_project_in_credentials_diffing if options[:enroll]
end
rescue ActiveSupport::MessageEncryptor::InvalidMessage
say credentials.content_path.read
end
private
def credentials
Rails.application.encrypted(content_path, key_path: key_path)
end
def ensure_encryption_key_has_been_added
encryption_key_file_generator.add_key_file(key_path)
encryption_key_file_generator.ignore_key_file(key_path)
end
def ensure_credentials_have_been_added
if options[:environment]
encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
else
credentials_generator.add_credentials_file_silently
end
end
def change_credentials_in_system_editor
credentials.change do |tmp_path|
system("#{ENV["EDITOR"]} #{tmp_path}")
end
end
def missing_credentials_message
if credentials.key.nil?
"Missing '#{key_path}' to decrypt credentials. See `rails credentials:help`"
else
"File '#{content_path}' does not exist. Use `rails credentials:edit` to change that."
end
end
def content_path
@content_path ||= options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
end
def key_path
options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
end
def extract_environment_from_path(path)
available_environments.find { |env| path.include? env } if path.end_with?(".yml.enc")
end
def encryption_key_file_generator
require "rails/generators"
require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
Rails::Generators::EncryptionKeyFileGenerator.new
end
def encrypted_file_generator
require "rails/generators"
require "rails/generators/rails/encrypted_file/encrypted_file_generator"
Rails::Generators::EncryptedFileGenerator.new
end
def credentials_generator
require "rails/generators"
require "rails/generators/rails/credentials/credentials_generator"
Rails::Generators::CredentialsGenerator.new
end
end
end
end
# frozen_string_literal: true
module Rails::Command::CredentialsCommand::Diffing # :nodoc:
def enroll_project_in_credentials_diffing
if enrolled?
true
else
gitattributes.write(<<~end_of_template, mode: "a")
config/credentials/*.yml.enc diff=rails_credentials
config/credentials.yml.enc diff=rails_credentials
end_of_template
say "Project successfully enrolled!"
say "Rails ensures the rails_credentials diff driver is set when running `credentials:edit`. See `credentials:help` for more."
end
end
def ensure_rails_credentials_driver_is_set
set_driver if enrolled? && !driver_configured?
end
private
def enrolled?
gitattributes.read.match?(/config\/credentials(\/\*)?\.yml\.enc diff=rails_credentials/)
rescue Errno::ENOENT
false
end
def driver_configured?
system "git config --get diff.rails_credentials.textconv", out: File::NULL
end
def set_driver
puts "running"
system "git config diff.rails_credentials.textconv 'bin/rails credentials:diff'"
end
def gitattributes
Rails.root.join(".gitattributes")
end
end
# frozen_string_literal: true
require "rails/generators"
require "rails/generators/rails/db/system/change/change_generator"
module Rails
module Command
module Db
module System
class ChangeCommand < Base # :nodoc:
class_option :to, desc: "The database system to switch to."
def initialize(positional_args, option_args, *)
@argv = positional_args + option_args
super
end
def perform
Rails::Generators::Db::System::ChangeGenerator.start(@argv)
end
end
end
end
end
end
# frozen_string_literal: true
require "active_support/deprecation"
require "active_support/core_ext/string/filters"
require "rails/command/environment_argument"
module Rails
class DBConsole
def self.start(*args)
new(*args).start
end
def initialize(options = {})
@options = options
end
def start
ENV["RAILS_ENV"] ||= @options[:environment] || environment
config = db_config.configuration_hash
case db_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 << db_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", db_config.database)
when "sqlite3"
args = []
args << "-#{@options[:mode]}" if @options[:mode]
args << "-header" if @options[:header]
args << File.expand_path(db_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].dup
logon << "/#{config[:password]}" if config[:password] && @options[:include_password]
logon << "@#{db_config.database}" if db_config.database
end
find_cmd_and_exec("sqlplus", logon)
when "sqlserver"
args = []
args += ["-D", "#{db_config.database}"] if db_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 #{db_config.database}."
end
end
def config
db_config.configuration_hash
end
deprecate config: "please use db_config.configuration_hash"
def db_config
return @db_config if defined?(@db_config)
# We need to check whether the user passed the database the
# first time around to show a consistent error message to people
# relying on 2-level database configuration.
@db_config = configurations.configs_for(env_name: environment, name: database)
unless @db_config
raise ActiveRecord::AdapterNotSpecified,
"'#{database}' database is not configured for '#{environment}'. Available configuration: #{configurations.inspect}"
end
@db_config
end
def environment
Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment
end
def database
@options.fetch(:database, "primary")
end
private
def configurations # :doc:
require APP_PATH
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Base.configurations
end
def find_cmd_and_exec(commands, *args) # :doc:
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)
begin
stat = File.stat(full_path_command)
rescue SystemCallError
else
stat.file? && stat.executable?
end
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
module Command
class DbconsoleCommand < Base # :nodoc:
include EnvironmentArgument
class_option :include_password, aliases: "-p", type: :boolean,
desc: "Automatically provide the password from database.yml"
class_option :mode, enum: %w( html list line column ), type: :string,
desc: "Automatically put the sqlite3 database in the specified mode (html, list, line, column)."
class_option :header, type: :boolean
class_option :connection, aliases: "-c", type: :string,
desc: "Specifies the connection to use."
class_option :database, aliases: "--db", type: :string,
desc: "Specifies the database to use."
def perform
extract_environment_option_from_argument
# RAILS_ENV needs to be set before config/application is required.
ENV["RAILS_ENV"] = options[:environment]
if options["connection"]
ActiveSupport::Deprecation.warn(<<-MSG.squish)
`connection` option is deprecated and will be removed in Rails 6.1. Please use `database` option instead.
MSG
options["database"] = options["connection"]
end
require_application_and_environment!
Rails::DBConsole.start(options)
end
end
end
end
# frozen_string_literal: true
require "rails/generators"
module Rails
module Command
class DestroyCommand < Base # :nodoc:
no_commands do
def help
require_application_and_environment!
load_generators
Rails::Generators.help self.class.command_name
end
end
def perform(*)
generator = args.shift
return help unless generator
require_application_and_environment!
load_generators
Rails::Generators.invoke generator, args, behavior: :revoke, destination_root: Rails::Command.root
end
end
end
end
# frozen_string_literal: true
require "rails/dev_caching"
module Rails
module Command
class DevCommand < Base # :nodoc:
no_commands do
def help
say "rails dev:cache # Toggle development mode caching on/off."
end
end
def cache
Rails::DevCaching.enable_by_file
end
end
end
end
# frozen_string_literal: true
require "pathname"
require "active_support"
require "rails/command/helpers/editor"
module Rails
module Command
class EncryptedCommand < Rails::Command::Base # :nodoc:
include Helpers::Editor
class_option :key, aliases: "-k", type: :string,
default: "config/master.key", desc: "The Rails.root relative path to the encryption key"
no_commands do
def help
say "Usage:\n #{self.class.banner}"
say ""
say self.class.desc
end
end
def edit(file_path)
require_application!
encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
ensure_editor_available(command: "bin/rails encrypted:edit") || (return)
ensure_encryption_key_has_been_added(options[:key]) if encrypted.key.nil?
ensure_encrypted_file_has_been_added(file_path, options[:key])
catch_editing_exceptions do
change_encrypted_file_in_system_editor(file_path, options[:key])
end
say "File encrypted and saved."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
say "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?"
end
def show(file_path)
require_application!
encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
say encrypted.read.presence || missing_encrypted_message(key: encrypted.key, key_path: options[:key], file_path: file_path)
end
private
def ensure_encryption_key_has_been_added(key_path)
encryption_key_file_generator.add_key_file(key_path)
encryption_key_file_generator.ignore_key_file(key_path)
end
def ensure_encrypted_file_has_been_added(file_path, key_path)
encrypted_file_generator.add_encrypted_file_silently(file_path, key_path)
end
def change_encrypted_file_in_system_editor(file_path, key_path)
Rails.application.encrypted(file_path, key_path: key_path).change do |tmp_path|
system("#{ENV["EDITOR"]} #{tmp_path}")
end
end
def encryption_key_file_generator
require "rails/generators"
require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
Rails::Generators::EncryptionKeyFileGenerator.new
end
def encrypted_file_generator
require "rails/generators"
require "rails/generators/rails/encrypted_file/encrypted_file_generator"
Rails::Generators::EncryptedFileGenerator.new
end
def missing_encrypted_message(key:, key_path:, file_path:)
if key.nil?
"Missing '#{key_path}' to decrypt data. See `bin/rails encrypted:help`"
else
"File '#{file_path}' does not exist. Use `bin/rails encrypted:edit #{file_path}` to change that."
end
end
end
end
end
# frozen_string_literal: true
require "rails/generators"
module Rails
module Command
class GenerateCommand < Base # :nodoc:
no_commands do
def help
require_application_and_environment!
load_generators
Rails::Generators.help self.class.command_name
end
end
def perform(*)
generator = args.shift
return help unless generator
require_application_and_environment!
load_generators
ARGV.replace(args) # set up ARGV for third-party libraries
Rails::Generators.invoke generator, args, behavior: :invoke, destination_root: Rails::Command.root
end
end
end
end
# frozen_string_literal: true
module Rails
module Command
class HelpCommand < Base # :nodoc:
hide_command!
def help(*)
say self.class.desc
Rails::Command.print_commands
end
end
end
end
# frozen_string_literal: true
require "rails/command/environment_argument"
module Rails
module Command
class InitializersCommand < Base # :nodoc:
include EnvironmentArgument
desc "initializers", "Print out all defined initializers in the order they are invoked by Rails."
def perform
extract_environment_option_from_argument
ENV["RAILS_ENV"] = options[:environment]
require_application_and_environment!
Rails.application.initializers.tsort_each do |initializer|
say "#{initializer.context_class}.#{initializer.name}"
end
end
end
end
end
# frozen_string_literal: true
module Rails
module Command
class NewCommand < Base # :nodoc:
no_commands do
def help
Rails::Command.invoke :application, [ "--help" ]
end
end
def perform(*)
say "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
say "Type 'rails' for help."
exit 1
end
end
end
end
# frozen_string_literal: true
require "rails/source_annotation_extractor"
module Rails
module Command
class NotesCommand < Base # :nodoc:
class_option :annotations, aliases: "-a", desc: "Filter by specific annotations, e.g. Foobar TODO", type: :array, default: Rails::SourceAnnotationExtractor::Annotation.tags
def perform(*)
require_application_and_environment!
deprecation_warning
display_annotations
end
private
def display_annotations
annotations = options[:annotations]
tag = (annotations.length > 1)
Rails::SourceAnnotationExtractor.enumerate annotations.join("|"), tag: tag, dirs: directories
end
def directories
Rails::SourceAnnotationExtractor::Annotation.directories + source_annotation_directories
end
def deprecation_warning
return if source_annotation_directories.empty?
ActiveSupport::Deprecation.warn("`SOURCE_ANNOTATION_DIRECTORIES` is deprecated and will be removed in Rails 6.1. You can add default directories by using config.annotations.register_directories instead.")
end
def source_annotation_directories
ENV["SOURCE_ANNOTATION_DIRECTORIES"].to_s.split(",")
end
end
end
end
# frozen_string_literal: true
module Rails
module Command
class PluginCommand < Base # :nodoc:
hide_command!
def help
run_plugin_generator %w( --help )
end
def self.banner(*) # :nodoc:
"#{executable} new [options]"
end
class_option :rc, type: :string, default: File.join("~", ".railsrc"),
desc: "Initialize the plugin command with previous defaults. Uses .railsrc in your home directory by default."
class_option :no_rc, desc: "Skip evaluating .railsrc."
def perform(type = nil, *plugin_args)
plugin_args << "--help" unless type == "new"
unless options.key?("no_rc") # Thor's not so indifferent access hash.
railsrc = File.expand_path(options[:rc])
if File.exist?(railsrc)
extra_args = File.read(railsrc).split(/\n+/).flat_map(&:split)
say "Using #{extra_args.join(" ")} from #{railsrc}"
plugin_args.insert(1, *extra_args)
end
end
run_plugin_generator plugin_args
end
private
def run_plugin_generator(plugin_args)
require "rails/generators"
require "rails/generators/rails/plugin/plugin_generator"
Rails::Generators::PluginGenerator.start plugin_args
end
end
end
end
# frozen_string_literal: true
module Rails
module Command
class RakeCommand < Base # :nodoc:
extend Rails::Command::Actions
namespace "rake"
class << self
def printing_commands
formatted_rake_tasks.map(&:first)
end
def perform(task, args, config)
require_rake
Rake.with_application do |rake|
load "rails/tasks.rb"
rake.init("rails", [task, *args])
rake.load_rakefile
if Rails.respond_to?(:root)
rake.options.suppress_backtrace_pattern = /\A(?!#{Regexp.quote(Rails.root.to_s)})/
end
rake.standard_exception_handling { rake.top_level }
end
end
private
def rake_tasks
require_rake
return @rake_tasks if defined?(@rake_tasks)
require_application!
Rake::TaskManager.record_task_metadata = true
Rake.application.instance_variable_set(:@name, "rails")
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
def require_rake
require "rake" # Defer booting Rake until we know it's needed.
end
end
end
end
end
# frozen_string_literal: true
require "rails/command"
module Rails
module Command
class RoutesCommand < Base # :nodoc:
class_option :controller, aliases: "-c", desc: "Filter by a specific controller, e.g. PostsController or Admin::PostsController."
class_option :grep, aliases: "-g", desc: "Grep routes by a specific pattern."
class_option :expanded, type: :boolean, aliases: "-E", desc: "Print routes expanded vertically with parts explained."
def perform(*)
require_application_and_environment!
require "action_dispatch/routing/inspector"
say inspector.format(formatter, routes_filter)
end
private
def inspector
ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
end
def formatter
if options.key?("expanded")
ActionDispatch::Routing::ConsoleFormatter::Expanded.new
else
ActionDispatch::Routing::ConsoleFormatter::Sheet.new
end
end
def routes_filter
options.symbolize_keys.slice(:controller, :grep)
end
end
end
end
# frozen_string_literal: true
require "rails/command/environment_argument"
module Rails
module Command
class RunnerCommand < Base # :nodoc:
include EnvironmentArgument
self.environment_desc = "The environment for the runner to operate under (test/development/production)"
no_commands do
def help
super
say self.class.desc
end
end
def self.banner(*)
"#{super} [<'Some.ruby(code)'> | <filename.rb> | -]"
end
def perform(code_or_file = nil, *command_argv)
extract_environment_option_from_argument
unless code_or_file
help
exit 1
end
ENV["RAILS_ENV"] = options[:environment]
require_application_and_environment!
Rails.application.load_runner
ARGV.replace(command_argv)
if code_or_file == "-"
eval($stdin.read, TOPLEVEL_BINDING, "stdin")
elsif File.exist?(code_or_file)
$0 = code_or_file
Kernel.load code_or_file
else
begin
eval(code_or_file, TOPLEVEL_BINDING, __FILE__, __LINE__)
rescue SyntaxError, NameError => e
error "Please specify a valid ruby command or the path of a script to run."
error "Run '#{self.class.executable} -h' for help."
error ""
error e
exit 1
end
end
end
end
end
end
# frozen_string_literal: true
require "active_support"
require "rails/secrets"
module Rails
module Command
class SecretsCommand < Rails::Command::Base # :nodoc:
no_commands do
def help
say "Usage:\n #{self.class.banner}"
say ""
say self.class.desc
end
end
def setup
deprecate_in_favor_of_credentials_and_exit
end
def edit
if ENV["EDITOR"].to_s.empty?
say "No $EDITOR to open decrypted secrets in. Assign one like this:"
say ""
say %(EDITOR="mate --wait" rails secrets:edit)
say ""
say "For editors that fork and exit immediately, it's important to pass a wait flag,"
say "otherwise the secrets will be saved immediately with no chance to edit."
return
end
require_application_and_environment!
Rails::Secrets.read_for_editing do |tmp_path|
system("#{ENV["EDITOR"]} #{tmp_path}")
end
say "New secrets encrypted and saved."
rescue Interrupt
say "Aborted changing encrypted secrets: nothing saved."
rescue Rails::Secrets::MissingKeyError => error
say error.message
rescue Errno::ENOENT => error
if /secrets\.yml\.enc/.match?(error.message)
deprecate_in_favor_of_credentials_and_exit
else
raise
end
end
def show
say Rails::Secrets.read
end
private
def deprecate_in_favor_of_credentials_and_exit
say "Encrypted secrets is deprecated in favor of credentials. Run:"
say "rails credentials:help"
exit 1
end
end
end
end
# frozen_string_literal: true
require "fileutils"
require "action_dispatch"
require "rails"
require "active_support/deprecation"
require "active_support/core_ext/string/filters"
require "active_support/core_ext/symbol/starts_ends_with"
require "rails/dev_caching"
require "rails/command/environment_argument"
module Rails
class Server < ::Rack::Server
class Options
def parse!(args)
Rails::Command::ServerCommand.new([], args).server_options
end
end
def initialize(options = nil)
@default_options = options || {}
super(@default_options)
set_environment
end
def opt_parser
Options.new
end
def set_environment
ENV["RAILS_ENV"] ||= options[:environment]
end
def start(after_stop_callback = nil)
trap(:INT) { exit }
create_tmp_directories
setup_dev_caching
log_to_stdout if options[:log_stdout]
super()
ensure
after_stop_callback.call if after_stop_callback
end
def serveable? # :nodoc:
server
true
rescue LoadError, NameError
false
end
def middleware
Hash.new([])
end
def default_options
super.merge(@default_options)
end
def served_url
"#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma?
end
private
def setup_dev_caching
if options[:environment] == "development"
Rails::DevCaching.enable_by_argument(options[:caching])
end
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
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT)
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
end
end
def use_puma?
server.to_s == "Rack::Handler::Puma"
end
end
module Command
class ServerCommand < Base # :nodoc:
include EnvironmentArgument
# Hard-coding a bunch of handlers here as we don't have a public way of
# querying them from the Rack::Handler registry.
RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn falcon)
DEFAULT_PORT = 3000
DEFAULT_PIDFILE = "tmp/pids/server.pid"
argument :using, optional: true
class_option :port, aliases: "-p", type: :numeric,
desc: "Runs Rails on the specified port - defaults to 3000.", banner: :port
class_option :binding, aliases: "-b", type: :string,
desc: "Binds Rails to the specified IP - defaults to 'localhost' in development and '0.0.0.0' in other environments'.",
banner: :IP
class_option :config, aliases: "-c", type: :string, default: "config.ru",
desc: "Uses a custom rackup configuration.", banner: :file
class_option :daemon, aliases: "-d", type: :boolean, default: false,
desc: "Runs server as a Daemon."
class_option :using, aliases: "-u", type: :string,
desc: "Specifies the Rack server used to run the application (thin/puma/webrick).", banner: :name
class_option :pid, aliases: "-P", type: :string,
desc: "Specifies the PID file - defaults to #{DEFAULT_PIDFILE}."
class_option :dev_caching, aliases: "-C", type: :boolean, default: nil,
desc: "Specifies whether to perform caching in development."
class_option :restart, type: :boolean, default: nil, hide: true
class_option :early_hints, type: :boolean, default: nil, desc: "Enables HTTP/2 early hints."
class_option :log_to_stdout, type: :boolean, default: nil, optional: true,
desc: "Whether to log to stdout. Enabled by default in development when not daemonized."
def initialize(args, local_options, *)
super
@original_options = local_options - %w( --restart )
deprecate_positional_rack_server_and_rewrite_to_option(@original_options)
end
def perform
extract_environment_option_from_argument
set_application_directory!
prepare_restart
Rails::Server.new(server_options).tap do |server|
# Require application after server sets environment to propagate
# the --environment option.
require APP_PATH
Dir.chdir(Rails.application.root)
if server.serveable?
print_boot_information(server.server, server.served_url)
after_stop_callback = -> { say "Exiting" unless options[:daemon] }
server.start(after_stop_callback)
else
say rack_server_suggestion(using)
end
end
end
no_commands do
def server_options
{
user_supplied_options: user_supplied_options,
server: using,
log_stdout: log_to_stdout?,
Port: port,
Host: host,
DoNotReverseLookup: true,
config: options[:config],
environment: environment,
daemonize: options[:daemon],
pid: pid,
caching: options[:dev_caching],
restart_cmd: restart_command,
early_hints: early_hints
}
end
end
private
def user_supplied_options
@user_supplied_options ||= begin
# Convert incoming options array to a hash of flags
# ["-p3001", "-C", "--binding", "127.0.0.1"] # => {"-p"=>true, "-C"=>true, "--binding"=>true}
user_flag = {}
@original_options.each do |command|
if command.start_with?("--")
option = command.split("=")[0]
user_flag[option] = true
elsif command =~ /\A(-.)/
user_flag[Regexp.last_match[0]] = true
end
end
# Collect all options that the user has explicitly defined so we can
# differentiate them from defaults
user_supplied_options = []
self.class.class_options.select do |key, option|
if option.aliases.any? { |name| user_flag[name] } || user_flag["--#{option.name}"]
name = option.name.to_sym
case name
when :port
name = :Port
when :binding
name = :Host
when :dev_caching
name = :caching
when :daemonize
name = :daemon
end
user_supplied_options << name
end
end
user_supplied_options << :Host if ENV["HOST"] || ENV["BINDING"]
user_supplied_options << :Port if ENV["PORT"]
user_supplied_options << :pid if ENV["PIDFILE"]
user_supplied_options.uniq
end
end
def port
options[:port] || ENV.fetch("PORT", DEFAULT_PORT).to_i
end
def host
if options[:binding]
options[:binding]
else
default_host = environment == "development" ? "localhost" : "0.0.0.0"
if ENV["HOST"] && !ENV["BINDING"]
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using the `HOST` environment variable to specify the IP is deprecated and will be removed in Rails 6.1.
Please use `BINDING` environment variable instead.
MSG
return ENV["HOST"]
end
ENV.fetch("BINDING", default_host)
end
end
def environment
options[:environment] || Rails::Command.environment
end
def restart_command
"bin/rails server #{@original_options.join(" ")} --restart"
end
def early_hints
options[:early_hints]
end
def log_to_stdout?
options.fetch(:log_to_stdout) do
options[:daemon].blank? && environment == "development"
end
end
def pid
File.expand_path(options[:pid] || ENV.fetch("PIDFILE", DEFAULT_PIDFILE))
end
def self.banner(*)
"rails server -u [thin/puma/webrick] [options]"
end
def prepare_restart
FileUtils.rm_f(pid) if options[:restart]
end
def deprecate_positional_rack_server_and_rewrite_to_option(original_options)
if using
ActiveSupport::Deprecation.warn(<<~MSG.squish)
Passing the Rack server name as a regular argument is deprecated
and will be removed in the next Rails version. Please, use the -u
option instead.
MSG
original_options.concat [ "-u", using ]
else
# Use positional internally to get around Thor's immutable options.
# TODO: Replace `using` occurrences with `options[:using]` after deprecation removal.
@using = options[:using]
end
end
def rack_server_suggestion(server)
if server.in?(RACK_SERVERS)
<<~MSG
Could not load server "#{server}". Maybe you need to the add it to the Gemfile?
gem "#{server}"
Run `bin/rails server --help` for more options.
MSG
else
suggestion = Rails::Command::Spellchecker.suggest(server, from: RACK_SERVERS)
suggestion_msg = "Maybe you meant #{suggestion.inspect}?" if suggestion
<<~MSG
Could not find server "#{server}". #{suggestion_msg}
Run `bin/rails server --help` for more options.
MSG
end
end
def print_boot_information(server, url)
say <<~MSG
=> Booting #{ActiveSupport::Inflector.demodulize(server)}
=> Rails #{Rails.version} application starting in #{Rails.env} #{url}
=> Run `bin/rails server --help` for more startup options
MSG
end
end
end
end
# frozen_string_literal: true
module Rails
module Command
class VersionCommand < Base # :nodoc:
def perform
Rails::Command.invoke :application, [ "--version" ]
end
end
end
end
# frozen_string_literal: true
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
#
# Middlewares can be moved from one place to another:
#
# config.middleware.move_before ActionDispatch::Flash, Magical::Unicorns
#
# This will move the <tt>Magical::Unicorns</tt> middleware before the
# <tt>ActionDispatch::Flash</tt>. You can also move it after:
#
# config.middleware.move_after 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 << -> middleware { middleware.send(__method__, *args, &block) }
end
ruby2_keywords(:insert_before) if respond_to?(:ruby2_keywords, true)
alias :insert :insert_before
def insert_after(*args, &block)
@operations << -> middleware { middleware.send(__method__, *args, &block) }
end
ruby2_keywords(:insert_after) if respond_to?(:ruby2_keywords, true)
def swap(*args, &block)
@operations << -> middleware { middleware.send(__method__, *args, &block) }
end
ruby2_keywords(:swap) if respond_to?(:ruby2_keywords, true)
def use(*args, &block)
@operations << -> middleware { middleware.send(__method__, *args, &block) }
end
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
def delete(*args, &block)
@delete_operations << -> middleware { middleware.send(__method__, *args, &block) }
end
def move_before(*args, &block)
@delete_operations << -> middleware { middleware.send(__method__, *args, &block) }
end
alias :move :move_before
def move_after(*args, &block)
@delete_operations << -> middleware { middleware.send(__method__, *args, &block) }
end
def unshift(*args, &block)
@operations << -> middleware { middleware.send(__method__, *args, &block) }
end
ruby2_keywords(:unshift) if respond_to?(:ruby2_keywords, true)
def merge_into(other) #:nodoc:
(@operations + @delete_operations).each do |operation|
operation.call(other)
end
other
end
def +(other) # :nodoc:
MiddlewareStackProxy.new(@operations + other.operations, @delete_operations + other.delete_operations)
end
protected
attr_reader :operations, :delete_operations
end
class Generators #:nodoc:
attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging, :api_only
attr_reader :hidden_namespaces, :after_generate_callbacks
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 = []
@after_generate_callbacks = []
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 after_generate(&block)
@after_generate_callbacks << block
end
def method_missing(method, *args)
method = method.to_s.delete_suffix("=").to_sym
if args.empty?
if method == :rails
return @options[method]
else
return @options[:rails][method]
end
end
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
# frozen_string_literal: true
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
Rails.application.reloader.reload!
true
end
end
end
# frozen_string_literal: true
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
# frozen_string_literal: true
require "fileutils"
module Rails
module DevCaching # :nodoc:
class << self
FILE = "tmp/caching-dev.txt"
def enable_by_file
FileUtils.mkdir_p("tmp")
if File.exist?(FILE)
delete_cache_file
puts "Development mode is no longer being cached."
else
create_cache_file
puts "Development mode is now being cached."
end
FileUtils.touch "tmp/restart.txt"
end
def enable_by_argument(caching)
FileUtils.mkdir_p("tmp")
if caching
create_cache_file
elsif caching == false && File.exist?(FILE)
delete_cache_file
end
end
private
def create_cache_file
FileUtils.touch FILE
end
def delete_cache_file
File.delete FILE
end
end
end
end
# frozen_string_literal: true
require "rails/railtie"
require "rails/engine/railties"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/object/try"
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
#
# Like railties, engines can access a config object which contains configuration shared by
# all railties and the application.
# Additionally, each engine can access <tt>autoload_paths</tt>, <tt>eager_load_paths</tt> and
# <tt>autoload_once_paths</tt> settings which are scoped to that engine.
#
# class MyEngine < Rails::Engine
# # Add a load path for this specific Engine
# config.autoload_paths << File.expand_path("lib/some/path", __dir__)
#
# 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 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:
#
# 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 explicitly 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 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.match?(%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))
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
def eager_load!
# Already done by Zeitwerk::Loader.eager_load_all. We need this guard to
# easily provide a compatible API for both zeitwerk and classic modes.
return if Rails.autoloaders.zeitwerk_enabled?
config.eager_load_paths.each do |load_path|
# Starts after load_path plus a slash, ends before ".rb".
relname_range = (load_path.to_s.length + 1)...-3
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
require_dependency file[relname_range]
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(&block)
@routes ||= ActionDispatch::Routing::RouteSet.new_with_config(config)
@routes.append(&block) 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
return unless seed_file
if config.try(:active_job)&.queue_adapter == :async
with_inline_jobs { load(seed_file) }
else
load(seed_file)
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 :set_load_path, before: :bootstrap_hook do |app|
_all_load_paths(app.config.add_autoload_paths_to_load_path).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)
config.autoload_paths.freeze
config.autoload_once_paths.freeze
end
initializer :set_eager_load_paths, before: :bootstrap_hook do
ActiveSupport::Dependencies._eager_load_paths.merge(config.eager_load_paths)
config.eager_load_paths.freeze
end
initializer :add_routing_paths do |app|
routing_paths = paths["config/routes.rb"].existent
external_paths = self.paths["config/routes"].paths
routes.draw_paths.concat(external_paths)
if routes? || routing_paths.any?
app.routes_reloader.paths.unshift(*routing_paths)
app.routes_reloader.route_sets << routes
app.routes_reloader.external_routes.unshift(*external_paths)
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 :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 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 run_tasks_blocks(*) #:nodoc:
super
paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
private
def load_config_initializer(initializer) # :doc:
ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do
load(initializer)
end
end
def with_inline_jobs
queue_adapter = config.active_job.queue_adapter
ActiveSupport.on_load(:active_job) do
self.queue_adapter = :inline
end
yield
ensure
ActiveSupport.on_load(:active_job) do
self.queue_adapter = queue_adapter
end
end
def has_migrations?
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
ActionDispatch::MiddlewareStack.new
end
def _all_autoload_once_paths
config.autoload_once_paths
end
def _all_autoload_paths
@_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
end
def _all_load_paths(add_autoload_paths_to_load_path)
@_all_load_paths ||= begin
load_paths = config.paths.load_paths
load_paths += _all_autoload_paths if add_autoload_paths_to_load_path
load_paths.uniq
end
end
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
# frozen_string_literal: true
unless defined?(APP_PATH)
if File.exist?(File.expand_path("test/dummy/config/application.rb", ENGINE_ROOT))
APP_PATH = File.expand_path("test/dummy/config/application", ENGINE_ROOT)
end
end
require "rails/commands"
# frozen_string_literal: true
- 17
require "rails/railtie/configuration"
- 17
module Rails
- 17
class Engine
- 17
class Configuration < ::Rails::Railtie::Configuration
- 17
attr_reader :root
- 17
attr_accessor :middleware, :javascript_path
- 17
attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths
- 17
def initialize(root = nil)
- 102
super()
- 102
@root = root
- 102
@generators = app_generators.dup
- 102
@middleware = Rails::Configuration::MiddlewareStackProxy.new
- 102
@javascript_path = "javascript"
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
#
- 17
def generators
- 32
@generators ||= Rails::Configuration::Generators.new
- 32
yield(@generators) if block_given?
- 32
@generators
end
- 17
def paths
- 17
@paths ||= begin
- 17
paths = Rails::Paths::Root.new(@root)
- 17
paths.add "app", eager_load: true,
glob: "{*,*/concerns}",
exclude: ["assets", javascript_path]
- 17
paths.add "app/assets", glob: "*"
- 17
paths.add "app/controllers", eager_load: true
- 17
paths.add "app/channels", eager_load: true, glob: "**/*_channel.rb"
- 17
paths.add "app/helpers", eager_load: true
- 17
paths.add "app/models", eager_load: true
- 17
paths.add "app/mailers", eager_load: true
- 17
paths.add "app/views"
- 17
paths.add "lib", load_path: true
- 17
paths.add "lib/assets", glob: "*"
- 17
paths.add "lib/tasks", glob: "**/*.rake"
- 17
paths.add "config"
- 17
paths.add "config/environments", glob: "#{Rails.env}.rb"
- 17
paths.add "config/initializers", glob: "**/*.rb"
- 17
paths.add "config/locales", glob: "*.{rb,yml}"
- 17
paths.add "config/routes.rb"
- 17
paths.add "config/routes", glob: "**/*.rb"
- 17
paths.add "db"
- 17
paths.add "db/migrate"
- 17
paths.add "db/seeds.rb"
- 17
paths.add "vendor", load_path: true
- 17
paths.add "vendor/assets", glob: "*"
- 17
paths
end
end
- 17
def root=(value)
- 33
@root = paths.path = Pathname.new(value).expand_path
end
- 17
def eager_load_paths
@eager_load_paths ||= paths.eager_load
end
- 17
def autoload_once_paths
@autoload_once_paths ||= paths.autoload_once
end
- 17
def autoload_paths
@autoload_paths ||= paths.autoload_paths
end
end
end
end
# frozen_string_literal: true
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
# frozen_string_literal: true
- 1
require "rails/generators"
- 1
require "rails/generators/rails/plugin/plugin_generator"
- 1
module Rails
- 1
class Engine
- 1
class Updater
- 1
class << self
- 1
def generator
@generator ||= Rails::Generators::PluginGenerator.new ["plugin"],
{ engine: true }, { destination_root: ENGINE_ROOT }
end
- 1
def run(action)
generator.send(action)
end
end
end
end
end
# frozen_string_literal: true
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 = 6
MINOR = 1
TINY = 0
PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
end
# frozen_string_literal: true
- 16
activesupport_path = File.expand_path("../../../activesupport/lib", __dir__)
- 16
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
- 16
require "thor/group"
- 16
require "rails/command"
- 16
require "active_support/core_ext/array/extract_options"
- 16
require "active_support/core_ext/enumerable"
- 16
require "active_support/core_ext/hash/deep_merge"
- 16
require "active_support/core_ext/module/attribute_accessors"
- 16
require "active_support/core_ext/string/indent"
- 16
require "active_support/core_ext/string/inflections"
- 16
module Rails
- 16
module Generators
- 16
include Rails::Command::Behavior
- 16
autoload :Actions, "rails/generators/actions"
- 16
autoload :ActiveModel, "rails/generators/active_model"
- 16
autoload :Base, "rails/generators/base"
- 16
autoload :Migration, "rails/generators/migration"
- 16
autoload :Database, "rails/generators/database"
- 16
autoload :AppName, "rails/generators/app_name"
- 16
autoload :NamedBase, "rails/generators/named_base"
- 16
autoload :ResourceHelpers, "rails/generators/resource_helpers"
- 16
autoload :TestCase, "rails/generators/test_case"
- 16
mattr_accessor :namespace
- 16
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",
}
}
- 16
DEFAULT_OPTIONS = {
rails: {
api: false,
assets: true,
force_plural: false,
helper: true,
integration_tool: nil,
orm: false,
resource_controller: :controller,
resource_route: true,
scaffold_controller: :scaffold_controller,
stylesheets: true,
stylesheet_engine: :css,
scaffold_stylesheet: true,
system_tests: nil,
test_framework: nil,
template_engine: :erb
}
}
- 16
class << self
- 16
def configure!(config) #:nodoc:
- 16
api_only! if config.api_only
- 16
no_color! unless config.colorize_logging
- 16
aliases.deep_merge! config.aliases
- 16
options.deep_merge! config.options
- 16
fallbacks.merge! config.fallbacks
- 16
templates_path.concat config.templates
- 16
templates_path.uniq!
- 16
hide_namespaces(*config.hidden_namespaces)
- 16
after_generate_callbacks.replace config.after_generate_callbacks
end
- 16
def templates_path #:nodoc:
- 55
@templates_path ||= []
end
- 16
def aliases #:nodoc:
- 204
@aliases ||= DEFAULT_ALIASES.dup
end
- 16
def options #:nodoc:
- 292
@options ||= DEFAULT_OPTIONS.dup
end
- 16
def after_generate_callbacks # :nodoc:
- 16
@after_generate_callbacks ||= []
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
- 16
def fallbacks
- 16
@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 helpers and assets
# so generators such as scaffold won't create them.
- 16
def api_only!
hide_namespaces "assets", "helper", "css", "js"
options[:rails].merge!(
api: true,
assets: false,
helper: false,
template_engine: nil
)
options[:mailer] ||= {}
options[:mailer][:template_engine] ||= :erb
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".
- 16
def hidden_namespaces
- 16
@hidden_namespaces ||= begin
- 16
orm = options[:rails][:orm]
- 16
test = options[:rails][:test_framework]
- 16
template = options[:rails][:template_engine]
- 16
css = options[:rails][:stylesheet_engine]
- 16
[
"rails",
"resource_route",
"#{orm}:migration",
"#{orm}:model",
"#{test}:controller",
"#{test}:helper",
"#{test}:integration",
"#{test}:system",
"#{test}:mailer",
"#{test}:model",
"#{test}:scaffold",
"#{test}:view",
"#{test}:job",
"#{template}:controller",
"#{template}:scaffold",
"#{template}:mailer",
"#{css}:scaffold",
"#{css}:assets",
"css:assets",
"css:scaffold",
"action_text:install",
"action_mailbox:install"
]
end
end
- 16
def hide_namespaces(*namespaces)
- 16
hidden_namespaces.concat(namespaces)
end
- 16
alias hide_namespace hide_namespaces
# Show help message with available generators.
- 16
def 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
- 16
def public_namespaces
lookup!
subclasses.map(&:namespace)
end
- 16
def print_generators
sorted_groups.each { |b, n| print_list(b, n) }
end
- 16
def 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.delete_prefix("rails:") }
rails.delete("app")
rails.delete("plugin")
rails.delete("encrypted_secrets")
rails.delete("encrypted_file")
rails.delete("encryption_key_file")
rails.delete("master_key")
rails.delete("credentials")
rails.delete("db:system:change")
hidden_namespaces.each { |n| groups.delete(n.to_s) }
[[ "rails", rails ]] + groups.sort.to_a
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.
- 16
def 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 = subclasses.index_by(&:namespace)
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.
- 16
def 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)
run_after_generate_callback if config[:behavior] == :invoke
else
options = sorted_groups.flat_map(&:last)
suggestion = Rails::Command::Spellchecker.suggest(namespace.to_s, from: options)
suggestion_msg = "Maybe you meant #{suggestion.inspect}?" if suggestion
puts <<~MSG
Could not find generator '#{namespace}'. #{suggestion_msg}
Run `bin/rails generate --help` for more options.
MSG
end
end
- 16
def add_generated_file(file) # :nodoc:
(@@generated_files ||= []) << file
file
end
- 16
private
- 16
def print_list(base, namespaces) # :doc:
namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
super
end
# Try fallbacks for the given base.
- 16
def invoke_fallbacks_for(name, base)
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
- 16
def command_type # :doc:
@command_type ||= "generator"
end
- 16
def lookup_paths # :doc:
@lookup_paths ||= %w( rails/generators generators )
end
- 16
def file_lookup_paths # :doc:
@file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ]
end
- 16
def run_after_generate_callback
if defined?(@@generated_files) && !@@generated_files.empty?
@after_generate_callbacks.each do |callback|
callback.call(@@generated_files)
end
@@generated_files = []
end
end
end
end
end
# frozen_string_literal: true
- 16
require "shellwords"
- 16
require "active_support/core_ext/kernel/reporting"
- 16
require "active_support/core_ext/string/strip"
- 16
module Rails
- 16
module Generators
- 16
module Actions
- 16
def initialize(*) # :nodoc:
super
@indentation = 0
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: "https://github.com/rails/rails"
# gem "RedCloth", ">= 4.1.0", "< 4.2.0"
- 16
def gem(*args)
options = args.extract_options!
name, *versions = 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.dup
if versions = versions.any? ? versions : options.delete(:version)
_versions = Array(versions)
_versions.each do |version|
parts << quote(version)
end
message << " (#{_versions.join(", ")})"
end
message = options[:git] if options[:git]
log :gemfile, message
parts << quote(options) unless options.empty?
in_root do
str = "gem #{parts.join(", ")}"
str = indentation + str
append_file_with_newline "Gemfile", str, verbose: false
end
end
# Wraps gem entries inside a group.
#
# gem_group :development, :test do
# gem "rspec-rails"
# end
- 16
def gem_group(*names, &block)
options = names.extract_options!
str = names.map(&:inspect)
str << quote(options) unless options.empty?
str = str.join(", ")
log :gemfile, "group #{str}"
in_root do
append_file_with_newline "Gemfile", "\ngroup #{str} do", force: true
with_indentation(&block)
append_file_with_newline "Gemfile", "end", force: true
end
end
- 16
def github(repo, options = {}, &block)
str = [quote(repo)]
str << quote(options) unless options.empty?
str = str.join(", ")
log :github, "github #{str}"
in_root do
if @indentation.zero?
append_file_with_newline "Gemfile", "\ngithub #{str} do", force: true
else
append_file_with_newline "Gemfile", "#{indentation}github #{str} do", force: true
end
with_indentation(&block)
append_file_with_newline "Gemfile", "#{indentation}end", 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
- 16
def add_source(source, options = {}, &block)
log :source, source
in_root do
if block
append_file_with_newline "Gemfile", "\nsource #{quote(source)} do", force: true
with_indentation(&block)
append_file_with_newline "Gemfile", "end", 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
- 16
def environment(data = nil, options = {})
sentinel = "class Application < Rails::Application\n"
env_file_sentinel = "Rails.application.configure do\n"
data ||= yield if block_given?
in_root do
if options[:env].nil?
inject_into_file "config/application.rb", optimize_indentation(data, 4), after: sentinel, verbose: false
else
Array(options[:env]).each do |env|
inject_into_file "config/environments/#{env}.rb", optimize_indentation(data, 2), after: env_file_sentinel, verbose: false
end
end
end
end
- 16
alias :application :environment
# Run a command in git.
#
# git :init
# git add: "this.file that.rb"
# git add: "onefile.rb", rm: "badfile.cxx"
- 16
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")
- 16
def vendor(filename, data = nil)
log :vendor, filename
data ||= yield if block_given?
create_file("vendor/#{filename}", optimize_indentation(data), verbose: false)
end
# Create a new file in the <tt>lib/</tt> 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")
- 16
def lib(filename, data = nil)
log :lib, filename
data ||= yield if block_given?
create_file("lib/#{filename}", optimize_indentation(data), verbose: false)
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"')
- 16
def rakefile(filename, data = nil)
log :rakefile, filename
data ||= yield if block_given?
create_file("lib/tasks/#{filename}", optimize_indentation(data), verbose: false)
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'")
- 16
def initializer(filename, data = nil)
log :initializer, filename
data ||= yield if block_given?
create_file("config/initializers/#{filename}", optimize_indentation(data), verbose: false)
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")
- 16
def generate(what, *args)
log :generate, what
options = args.extract_options!
options[:abort_on_failure] = !options[:inline]
rails_command "generate #{what} #{args.join(" ")}", options
end
# Runs the supplied rake task (invoked with 'rake ...')
#
# rake("db:migrate")
# rake("db:migrate", env: "production")
# rake("gems:install", sudo: true)
# rake("gems:install", capture: true)
- 16
def rake(command, options = {})
execute_command :rake, command, options
end
# Runs the supplied rake task (invoked with 'rails ...')
#
# rails_command("db:migrate")
# rails_command("db:migrate", env: "production")
# rails_command("gems:install", sudo: true)
# rails_command("gems:install", capture: true)
- 16
def rails_command(command, options = {})
if options[:inline]
log :rails, command
command, *args = Shellwords.split(command)
in_root do
silence_warnings do
::Rails::Command.invoke(command, args, **options)
end
end
else
execute_command :rails, command, options
end
end
# Make an entry in Rails routing file <tt>config/routes.rb</tt>
#
# route "root 'welcome#index'"
# route "root 'admin#index'", namespace: :admin
- 16
def route(routing_code, namespace: nil)
routing_code = Array(namespace).reverse.reduce(routing_code) do |code, ns|
"namespace :#{ns} do\n#{indent(code, 2)}\nend"
end
log :route, routing_code
sentinel = /\.routes\.draw do\s*\n/m
in_root do
inject_into_file "config/routes.rb", optimize_indentation(routing_code, 2), after: sentinel, verbose: false, force: false
end
end
# Reads the given file at the source root and prints it in the console.
#
# readme "README"
- 16
def readme(path)
log File.read(find_in_source_paths(path))
end
- 16
private
# 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.
- 16
def log(*args) # :doc:
if args.size == 1
say args.first.to_s unless options.quiet?
else
args << (behavior == :invoke ? :green : :red)
say_status(*args)
end
end
# Runs the supplied command using either "rake ..." or "rails ..."
# based on the executor parameter provided.
- 16
def execute_command(executor, command, options = {}) # :doc:
log executor, command
sudo = options[:sudo] && !Gem.win_platform? ? "sudo " : ""
config = {
env: { "RAILS_ENV" => (options[:env] || ENV["RAILS_ENV"] || "development") },
verbose: false,
capture: options[:capture],
abort_on_failure: options[:abort_on_failure],
}
in_root { run("#{sudo}#{extify(executor)} #{command}", config) }
end
# Add an extension to the given name based on the platform.
- 16
def extify(name) # :doc:
if Gem.win_platform?
"#{name}.bat"
else
name
end
end
# Surround string with single quotes if there is no quotes.
# Otherwise fall back to double quotes
- 16
def quote(value) # :doc:
if value.respond_to? :each_pair
return value.map do |k, v|
"#{k}: #{quote(v)}"
end.join(", ")
end
return value.inspect unless value.is_a? String
if value.include?("'")
value.inspect
else
"'#{value}'"
end
end
# Returns optimized string with indentation
- 16
def optimize_indentation(value, amount = 0) # :doc:
return "#{value}\n" unless value.is_a?(String)
"#{value.strip_heredoc.indent(amount).chomp}\n"
end
# Indent the +Gemfile+ to the depth of @indentation
- 16
def indentation # :doc:
" " * @indentation
end
# Manage +Gemfile+ indentation for a DSL action block
- 16
def with_indentation(&block) # :doc:
@indentation += 1
instance_eval(&block)
ensure
@indentation -= 1
end
# Append string to a file with a newline if necessary
- 16
def append_file_with_newline(path, str, options = {})
gsub_file path, /\n?\z/, options do |match|
match.end_with?("\n") ? "" : "\n#{str}\n"
end
end
end
end
end
# frozen_string_literal: true
- 1
require "fileutils"
- 1
require "thor/actions"
- 1
module Rails
- 1
module Generators
- 1
module Actions
- 1
class CreateMigration < Thor::Actions::CreateFile #:nodoc:
- 1
def migration_dir
File.dirname(@destination)
end
- 1
def migration_file_name
@base.migration_file_name
end
- 1
def identical?
exists? && File.binread(existing_migration) == render
end
- 1
def invoke!
invoked_file = super
File.exist?(@destination) ? invoked_file : relative_existing_migration
end
- 1
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
- 1
def relative_existing_migration
base.relative_to_original_destination_root(existing_migration)
end
- 1
def existing_migration
@existing_migration ||= begin
@base.class.migration_exists?(migration_dir, migration_file_name) ||
File.exist?(@destination) && @destination
end
end
- 1
alias :exists? :existing_migration
- 1
private
- 1
def on_conflict_behavior # :doc:
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
- 1
def say_status(status, color, message = relative_destination) # :doc:
base.shell.say_status(status, message, color) if config[:verbose]
end
end
end
end
end
# frozen_string_literal: true
- 3
module Rails
- 3
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.
#
- 3
class ActiveModel
- 3
attr_reader :name
- 3
def initialize(name)
@name = name
end
# GET index
- 3
def self.all(klass)
"#{klass}.all"
end
# GET show
# GET edit
# PATCH/PUT update
# DELETE destroy
- 3
def self.find(klass, params = nil)
"#{klass}.find(#{params})"
end
# GET new
# POST create
- 3
def self.build(klass, params = nil)
if params
"#{klass}.new(#{params})"
else
"#{klass}.new"
end
end
# POST create
- 3
def save
"#{name}.save"
end
# PATCH/PUT update
- 3
def update(params = nil)
"#{name}.update(#{params})"
end
# POST create
# PATCH/PUT update
- 3
def errors
"#{name}.errors"
end
# DELETE destroy
- 3
def destroy
"#{name}.destroy"
end
end
end
end
# frozen_string_literal: true
- 3
require "fileutils"
- 3
require "digest/md5"
- 3
require "rails/version" unless defined?(Rails::VERSION)
- 3
require "open-uri"
- 3
require "uri"
- 3
require "rails/generators"
- 3
require "active_support/core_ext/array/extract_options"
- 3
module Rails
- 3
module Generators
- 3
class AppBase < Base # :nodoc:
- 3
include Database
- 3
include AppName
- 3
attr_accessor :rails_template
- 3
add_shebang_option!
- 3
argument :app_path, type: :string
- 3
def self.strict_args_position
false
end
- 3
def self.add_shared_options_for(name)
- 4
class_option :template, type: :string, aliases: "-m",
desc: "Path to some #{name} template (can be a filesystem path or URL)"
- 4
class_option :database, type: :string, aliases: "-d", default: "sqlite3",
desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
- 4
class_option :skip_gemfile, type: :boolean, default: false,
desc: "Don't create a Gemfile"
- 4
class_option :skip_git, type: :boolean, aliases: "-G", default: false,
desc: "Skip .gitignore file"
- 4
class_option :skip_keeps, type: :boolean, default: false,
desc: "Skip source control .keep files"
- 4
class_option :skip_action_mailer, type: :boolean, aliases: "-M",
default: false,
desc: "Skip Action Mailer files"
- 4
class_option :skip_action_mailbox, type: :boolean, default: false,
desc: "Skip Action Mailbox gem"
- 4
class_option :skip_action_text, type: :boolean, default: false,
desc: "Skip Action Text gem"
- 4
class_option :skip_active_record, type: :boolean, aliases: "-O", default: false,
desc: "Skip Active Record files"
- 4
class_option :skip_active_job, type: :boolean, default: false,
desc: "Skip Active Job"
- 4
class_option :skip_active_storage, type: :boolean, default: false,
desc: "Skip Active Storage files"
- 4
class_option :skip_puma, type: :boolean, aliases: "-P", default: false,
desc: "Skip Puma related files"
- 4
class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false,
desc: "Skip Action Cable files"
- 4
class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false,
desc: "Skip Sprockets files"
- 4
class_option :skip_spring, type: :boolean, default: false,
desc: "Don't install Spring application preloader"
- 4
class_option :skip_listen, type: :boolean, default: false,
desc: "Don't generate configuration that depends on the listen gem"
- 4
class_option :skip_javascript, type: :boolean, aliases: "-J", default: name == "plugin",
desc: "Skip JavaScript files"
- 4
class_option :skip_turbolinks, type: :boolean, default: false,
desc: "Skip turbolinks gem"
- 4
class_option :skip_jbuilder, type: :boolean, default: false,
desc: "Skip jbuilder gem"
- 4
class_option :skip_test, type: :boolean, aliases: "-T", default: false,
desc: "Skip test files"
- 4
class_option :skip_system_test, type: :boolean, default: false,
desc: "Skip system test files"
- 4
class_option :skip_bootsnap, type: :boolean, default: false,
desc: "Skip bootsnap gem"
- 4
class_option :dev, type: :boolean, default: false,
desc: "Set up the #{name} with Gemfile pointing to your Rails checkout"
- 4
class_option :edge, type: :boolean, default: false,
desc: "Set up the #{name} with Gemfile pointing to Rails repository"
- 4
class_option :master, type: :boolean, default: false,
desc: "Set up the #{name} with Gemfile pointing to Rails repository master branch"
- 4
class_option :rc, type: :string, default: nil,
desc: "Path to file containing extra configuration options for rails command"
- 4
class_option :no_rc, type: :boolean, default: false,
desc: "Skip loading of extra configuration options from .railsrc file"
- 4
class_option :help, type: :boolean, aliases: "-h", group: :rails,
desc: "Show this help message and quit"
end
- 3
def initialize(*args)
@gem_filter = lambda { |gem| true }
@extra_entries = []
super
end
- 3
private
- 3
def gemfile_entry(name, *args) # :doc:
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
- 3
def gemfile_entries # :doc:
[rails_gemfile_entry,
database_gemfile_entry,
web_server_gemfile_entry,
assets_gemfile_entry,
webpacker_gemfile_entry,
javascript_gemfile_entry,
jbuilder_gemfile_entry,
psych_gemfile_entry,
cable_gemfile_entry,
@extra_entries].flatten.find_all(&@gem_filter)
end
- 3
def add_gem_entry_filter # :doc:
@gem_filter = lambda { |next_filter, entry|
yield(entry) && next_filter.call(entry)
}.curry[@gem_filter]
end
- 3
def builder # :doc:
@builder ||= begin
builder_class = get_builder_class
builder_class.include(ActionMethods)
builder_class.new(self)
end
end
- 3
def build(meth, *args) # :doc:
builder.send(meth, *args) if builder.respond_to?(meth)
end
- 3
def create_root # :doc:
valid_const?
empty_directory "."
FileUtils.cd(destination_root) unless options[:pretend]
end
- 3
def apply_rails_template # :doc:
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
- 3
def set_default_accessors! # :doc:
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
- 3
def database_gemfile_entry # :doc:
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
- 3
def web_server_gemfile_entry # :doc:
return [] if options[:skip_puma]
comment = "Use Puma as the app server"
GemfileEntry.new("puma", "~> 4.1", comment)
end
- 3
def include_all_railties? # :doc:
[
options.values_at(
:skip_active_record,
:skip_action_mailer,
:skip_test,
:skip_sprockets,
:skip_action_cable,
:skip_active_job
),
skip_active_storage?,
skip_action_mailbox?,
skip_action_text?
].flatten.none?
end
- 3
def comment_if(value) # :doc:
question = "#{value}?"
comment =
if respond_to?(question, true)
send(question)
else
options[value]
end
comment ? "# " : ""
end
- 3
def keeps? # :doc:
!options[:skip_keeps]
end
- 3
def sqlite3? # :doc:
!options[:skip_active_record] && options[:database] == "sqlite3"
end
- 3
def skip_active_storage? # :doc:
options[:skip_active_storage] || options[:skip_active_record]
end
- 3
def skip_action_mailbox? # :doc:
options[:skip_action_mailbox] || skip_active_storage?
end
- 3
def skip_action_text? # :doc:
options[:skip_action_text] || skip_active_storage?
end
- 3
def skip_dev_gems? # :doc:
options[:skip_dev_gems]
end
- 3
class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
- 3
def initialize(name, version, comment, options = {}, commented_out = false)
super
end
- 3
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
- 3
def self.version(name, version, comment = nil)
new(name, version, comment)
end
- 3
def self.path(name, path, comment = nil)
new(name, nil, comment, path: path)
end
- 3
def version
version = super
if version.is_a?(Array)
version.join("', '")
else
version
end
end
end
- 3
def rails_gemfile_entry
if options.dev?
[
GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH)
]
elsif options.edge?
[
GemfileEntry.github("rails", "rails/rails")
]
elsif options.master?
[
GemfileEntry.github("rails", "rails/rails", "master")
]
else
[GemfileEntry.version("rails",
rails_version_specifier,
"Bundle edge Rails instead: gem 'rails', github: 'rails/rails'")]
end
end
- 3
def rails_version_specifier(gem_version = Rails.gem_version)
if gem_version.segments.size == 3 || gem_version.release.segments.size == 3
# ~> 1.2.3
# ~> 1.2.3.pre4
"~> #{gem_version}"
else
# ~> 1.2.3, >= 1.2.3.4
# ~> 1.2.3, >= 1.2.3.4.pre5
patch = gem_version.segments[0, 3].join(".")
["~> #{patch}", ">= #{gem_version}"]
end
end
- 3
def assets_gemfile_entry
return [] if options[:skip_sprockets]
GemfileEntry.version("sass-rails", ">= 6", "Use SCSS for stylesheets")
end
- 3
def webpacker_gemfile_entry
return [] if options[:skip_javascript]
if options.dev? || options.edge? || options.master?
GemfileEntry.github "webpacker", "rails/webpacker", nil, "Use development version of Webpacker"
else
GemfileEntry.version "webpacker", "~> 5.0", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
end
end
- 3
def jbuilder_gemfile_entry
return [] if options[:skip_jbuilder]
comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
GemfileEntry.new "jbuilder", "~> 2.7", comment, {}, options[:api]
end
- 3
def javascript_gemfile_entry
if options[:skip_javascript] || options[:skip_turbolinks]
[]
else
[ GemfileEntry.version("turbolinks", "~> 5",
"Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") ]
end
end
- 3
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
- 3
def cable_gemfile_entry
return [] if options[:skip_action_cable]
comment = "Use Redis adapter to run Action Cable in production"
gems = []
gems << GemfileEntry.new("redis", "~> 4.0", comment, {}, true)
gems
end
- 3
def bundle_command(command, env = {})
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.
#
# Thanks to James Tucker for the Gem tricks involved in this call.
_bundle_command = Gem.bin_path("bundler", "bundle")
require "bundler"
Bundler.with_original_env do
exec_bundle_command(_bundle_command, command, env)
end
end
- 3
def exec_bundle_command(bundle_command, command, env)
full_command = %Q["#{Gem.ruby}" "#{bundle_command}" #{command}]
if options[:quiet]
system(env, full_command, out: File::NULL)
else
system(env, full_command)
end
end
- 3
def bundle_install?
!(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
end
- 3
def spring_install?
!options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
end
- 3
def webpack_install?
!(options[:skip_javascript] || options[:skip_webpack_install])
end
- 3
def depends_on_system_test?
!(options[:skip_system_test] || options[:skip_test] || options[:api])
end
- 3
def depend_on_listen?
!options[:skip_listen] && os_supports_listen_out_of_the_box?
end
- 3
def depend_on_bootsnap?
!options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION)
end
- 3
def os_supports_listen_out_of_the_box?
/darwin|linux/.match?(RbConfig::CONFIG["host_os"])
end
- 3
def run_bundle
bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1") if bundle_install?
end
- 3
def run_webpack
if webpack_install?
rails_command "webpacker:install"
if options[:webpack] && options[:webpack] != "webpack"
rails_command "webpacker:install:#{options[:webpack]}"
end
end
end
- 3
def generate_bundler_binstub
if bundle_install?
bundle_command("binstubs bundler")
end
end
- 3
def generate_spring_binstub
if bundle_install? && spring_install?
bundle_command("exec spring binstub")
end
end
- 3
def empty_directory_with_keep_file(destination, config = {})
empty_directory(destination, config)
keep_file(destination)
end
- 3
def keep_file(destination)
create_file("#{destination}/.keep") if keeps?
end
end
end
end
# frozen_string_literal: true
- 3
module Rails
- 3
module Generators
- 3
module AppName # :nodoc:
- 3
RESERVED_NAMES = %w(application destroy plugin runner test)
- 3
private
- 3
def app_name
@app_name ||= original_app_name.tr('\\', "").tr("-. ", "_")
end
- 3
def original_app_name
@original_app_name ||= defined_app_const_base? ? defined_app_name : File.basename(destination_root)
end
- 3
def defined_app_name
defined_app_const_base.underscore
end
- 3
def defined_app_const_base
Rails.respond_to?(:application) && defined?(Rails::Application) &&
Rails.application.is_a?(Rails::Application) && Rails.application.class.name.chomp("::Application")
end
- 3
alias :defined_app_const_base? :defined_app_const_base
- 3
def app_const_base
@app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, "_").squeeze("_").camelize
end
- 3
alias :camelized :app_const_base
- 3
def app_const
@app_const ||= "#{app_const_base}::Application"
end
- 3
def valid_const?
if /^\d/.match?(app_const)
raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers."
elsif RESERVED_NAMES.include?(original_app_name)
raise Error, "Invalid application name #{original_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 #{original_app_name}, constant #{app_const_base} is already in use. Please choose another application name."
end
end
end
end
end
# frozen_string_literal: true
- 16
begin
- 16
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
- 16
module Rails
- 16
module Generators
- 16
class Error < Thor::Error # :nodoc:
end
- 16
class Base < Thor::Group
- 16
include Thor::Actions
- 16
include Rails::Generators::Actions
- 16
class_option :skip_namespace, type: :boolean, default: false,
desc: "Skip namespace (affects only isolated engines)"
- 16
class_option :skip_collision_check, type: :boolean, default: false,
desc: "Skip collision check"
- 16
add_runtime_options!
- 16
strict_args_position!
- 16
def self.exit_on_failure? # :nodoc:
false
end
# Returns the source root for this generator using default_source_root as default.
- 16
def self.source_root(path = nil)
- 42
@_source_root = path if path
- 42
@_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.
- 16
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.
- 16
def self.namespace(name = nil)
return super if name
@namespace ||= super.delete_suffix("_generator").sub(/:generators:/, ":")
end
# Convenience method to hide this generator from the available ones when
# running rails generator command.
- 16
def self.hide!
Rails::Generators.hide_namespace(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:
#
# bin/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:
#
# bin/rails generate controller Account --skip-test-framework
#
# Or similarly:
#
# bin/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:
#
# bin/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
#
- 16
def self.hook_for(*names, &block)
- 23
options = names.extract_options!
- 23
in_base = options.delete(:in) || base_name
- 23
as_hook = options.delete(:as) || generator_name
- 23
names.each do |name|
- 31
unless class_options.key?(name)
- 24
defaults = if options[:type] == :boolean
{}
- 24
elsif [true, false].include?(default_value_for_option(name, options))
- 2
{ banner: "" }
else
- 22
{ desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
end
- 24
class_option(name, defaults.merge!(options))
end
- 31
hooks[name] = [ in_base, as_hook ]
- 31
invoke_from_option(name, options, &block)
end
end
# Remove a previously added hook.
#
# remove_hook_for :orm
- 16
def self.remove_hook_for(*names)
- 1
remove_invocation(*names)
- 1
names.each do |name|
- 1
hooks.delete(name)
end
end
# Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
- 16
def self.class_option(name, options = {}) #:nodoc:
- 188
options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
- 188
options[:aliases] = default_aliases_for_option(name, options)
- 188
options[:default] = default_value_for_option(name, options)
- 188
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.
- 16
def self.default_source_root
- 39
return unless base_name && generator_name
- 39
return unless default_generator_root
- 19
path = File.join(default_generator_root, "templates")
- 19
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.
- 16
def self.base_root
- 58
__dir__
end
# Cache source root and add lib/generators/base/generator/templates to
# source paths.
- 16
def self.inherited(base) #:nodoc:
- 39
super
# Invoke source_root so the default_source_root is set.
- 39
base.source_root
- 39
if base.name && !base.name.end_with?("Base")
- 23
Rails::Generators.subclasses << base
- 23
Rails::Generators.templates_path.each do |path|
- 23
if base.name.include?("::")
- 23
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
- 16
private
# Check whether the given class names are already taken by user
# application or Ruby on Rails.
- 16
def class_collisions(*class_names)
return unless behavior == :invoke
return if options.skip_collision_check?
return if options.force?
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 or use --skip-collision-check " \
"or --force to skip this check and run this generator again."
end
end
end
# Takes in an array of nested modules and extracts the last module
- 16
def extract_last_module(nesting) # :doc:
nesting.inject(Object) do |last_module, nest|
break unless last_module.const_defined?(nest, false)
last_module.const_get(nest)
end
end
# Wrap block with namespace of current application
# if namespace exists and is not skipped
- 16
def module_namespacing(&block) # :doc:
content = capture(&block)
content = wrap_with_namespace(content) if namespaced?
concat(content)
end
- 16
def indent(content, multiplier = 2) # :doc:
spaces = " " * multiplier
content.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join
end
- 16
def wrap_with_namespace(content) # :doc:
content = indent(content).chomp
"module #{namespace.name}\n#{content}\nend\n"
end
- 16
def namespace # :doc:
Rails::Generators.namespace
end
- 16
def namespaced? # :doc:
!options[:skip_namespace] && namespace
end
- 16
def namespace_dirs
@namespace_dirs ||= namespace.name.split("::").map(&:underscore)
end
- 16
def namespaced_path # :doc:
@namespaced_path ||= namespace_dirs.join("/")
end
# Use Rails default banner.
- 16
def self.banner # :doc:
"rails generate #{namespace.delete_prefix("rails:")} #{arguments.map(&:usage).join(' ')} [options]".gsub(/\s+/, " ")
end
# Sets the base_name taking into account the current class namespace.
- 16
def self.base_name # :doc:
- 943
@base_name ||= begin
- 39
if base = name.to_s.split("::").first
- 39
base.underscore
end
end
end
# Removes the namespaces and get the generator name. For example,
# Rails::Generators::ModelGenerator will return "model" as generator name.
- 16
def self.generator_name # :doc:
- 938
@generator_name ||= begin
- 39
if generator = name.to_s.split("::").last
- 39
generator.delete_suffix!("Generator")
- 39
generator.underscore
end
end
end
# Returns the default value for the option name given doing a lookup in
# Rails::Generators.options.
- 16
def self.default_value_for_option(name, options) # :doc:
- 212
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.
- 16
def self.default_aliases_for_option(name, options) # :doc:
- 188
default_for_option(Rails::Generators.aliases, name, options, options[:aliases])
end
# Returns default for the option name given doing a lookup in config.
- 16
def self.default_for_option(config, name, options, default) # :doc:
- 400
if generator_name && (c = config[generator_name.to_sym]) && c.key?(name)
c[name]
- 400
elsif base_name && (c = config[base_name.to_sym]) && c.key?(name)
- 95
c[name]
- 305
elsif config[:rails].key?(name)
config[:rails][name]
else
- 305
default
end
end
# Keep hooks configuration that are used on prepare_for_invocation.
- 16
def self.hooks #:nodoc:
- 61
@hooks ||= from_superclass(:hooks, {})
end
# Prepare class invocation to search on Rails namespace if a previous
# added hook is being used.
- 16
def self.prepare_for_invocation(name, value) #:nodoc:
return super unless value.is_a?(String) || value.is_a?(Symbol)
if value && constants = 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.
- 16
def self.add_shebang_option! # :doc:
- 3
class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command,
desc: "Path to the Ruby binary of your choice", banner: "PATH"
- 3
no_tasks {
- 3
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
- 16
def self.usage_path # :doc:
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
- 16
def self.default_generator_root # :doc:
- 58
path = File.expand_path(File.join(base_name, generator_name), base_root)
- 58
path if File.exist?(path)
end
end
end
end
# frozen_string_literal: true
require "rails/generators/named_base"
module Css # :nodoc:
module Generators # :nodoc:
class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
source_root File.expand_path("templates", __dir__)
def copy_stylesheet
copy_file "stylesheet.css", File.join("app/assets/stylesheets", class_path, "#{file_name}.css")
end
end
end
end
# frozen_string_literal: true
require "rails/generators/named_base"
module Css # :nodoc:
module Generators # :nodoc:
class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc:
source_root Rails::Generators::ScaffoldGenerator.source_root
# 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
copy_file "scaffold.css", "app/assets/stylesheets/scaffold.css"
end
end
end
end
# frozen_string_literal: true
- 3
module Rails
- 3
module Generators
- 3
module Database # :nodoc:
- 3
JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
- 3
DATABASES = %w( mysql postgresql sqlite3 oracle sqlserver ) + JDBC_DATABASES
- 3
def initialize(*)
super
convert_database_option_for_jruby
end
- 3
def gem_for_database(database = options[:database])
case database
when "mysql" then ["mysql2", ["~> 0.5"]]
when "postgresql" then ["pg", ["~> 1.1"]]
when "sqlite3" then ["sqlite3", ["~> 1.4"]]
when "oracle" then ["activerecord-oracle_enhanced-adapter", nil]
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 [database, nil]
end
end
- 3
def convert_database_option_for_jruby
if defined?(JRUBY_VERSION)
opt = options.dup
case opt[:database]
when "postgresql" then opt[:database] = "jdbcpostgresql"
when "mysql" then opt[:database] = "jdbcmysql"
when "sqlite3" then opt[:database] = "jdbcsqlite3"
end
self.options = opt.freeze
end
end
- 3
private
- 3
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 Gem.win_platform?
end
end
end
end
# frozen_string_literal: true
require "rails/generators/named_base"
module Erb # :nodoc:
module Generators # :nodoc:
class Base < Rails::Generators::NamedBase #:nodoc:
private
def formats
[format]
end
def format
:html
end
def handler
:erb
end
def filename_with_extensions(name, file_format = format)
[name, file_format, handler].compact.join(".")
end
end
end
end
# frozen_string_literal: true
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
# frozen_string_literal: true
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 behavior == :invoke
formats.each do |format|
layout_path = File.join("app/views/layouts", class_path, filename_with_extensions("mailer", format))
template filename_with_extensions(:layout, format), layout_path unless File.exist?(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
private
def formats
[:text, :html]
end
def file_name
@_file_name ||= super.sub(/_mailer\z/i, "")
end
end
end
end
# frozen_string_literal: true
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
private
def available_views
%w(index edit show new _form)
end
end
end
end
# frozen_string_literal: true
- 13
require "active_support/time"
- 13
require "active_support/deprecation"
- 13
module Rails
- 13
module Generators
- 13
class GeneratedAttribute # :nodoc:
- 13
INDEX_OPTIONS = %w(index uniq)
- 13
UNIQ_INDEX_OPTIONS = %w(uniq)
- 13
attr_accessor :name, :type
- 13
attr_reader :attr_options
- 13
attr_writer :index_name
- 13
class << self
- 13
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)
if UNIQ_INDEX_OPTIONS.include?(has_index)
attr_options[:index] = { unique: true }
end
end
new(name, type, has_index, attr_options)
end
- 13
def reference?(type)
[:references, :belongs_to].include? type
end
- 13
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
- 13
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] }]
if options[:required]
ActiveSupport::Deprecation.warn("Passing {required} option has no effect on the model generator. It will be removed in Rails 6.1.\n")
options.delete(:required)
end
return type, options
else
return type, {}
end
end
end
- 13
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
- 13
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 :rich_text then :rich_text_area
when :boolean then :check_box
when :attachment, :attachments then :file_field
else
:text_field
end
end
- 13
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,
:attachment, :attachments,
:rich_text then nil
else
""
end
end
- 13
def plural_name
name.delete_suffix("_id").pluralize
end
- 13
def singular_name
name.delete_suffix("_id").singularize
end
- 13
def human_name
name.humanize
end
- 13
def index_name
@index_name ||= if polymorphic?
%w(id type).map { |t| "#{name}_#{t}" }
else
column_name
end
end
- 13
def column_name
@column_name ||= reference? ? "#{name}_id" : name
end
- 13
def foreign_key?
name.end_with?("_id")
end
- 13
def reference?
self.class.reference?(type)
end
- 13
def polymorphic?
attr_options[:polymorphic]
end
- 13
def required?
reference? && Rails.application.config.active_record.belongs_to_required_by_default
end
- 13
def has_index?
@has_index
end
- 13
def has_uniq_index?
@has_uniq_index
end
- 13
def password_digest?
name == "password" && type == :digest
end
- 13
def token?
type == :token
end
- 13
def rich_text?
type == :rich_text
end
- 13
def attachment?
type == :attachment
end
- 13
def attachments?
type == :attachments
end
- 13
def virtual?
rich_text? || attachment? || attachments?
end
- 13
def inject_options
(+"").tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
end
- 13
def inject_index_options
has_uniq_index? ? ", unique: true" : ""
end
- 13
def options_for_migration
@attr_options.dup.tap do |options|
if required?
options[:null] = false
end
if reference? && !polymorphic?
options[:foreign_key] = true
end
end
end
end
end
end
# frozen_string_literal: true
- 1
require "active_support/concern"
- 1
require "rails/generators/actions/create_migration"
- 1
module Rails
- 1
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.
- 1
module Migration
- 1
extend ActiveSupport::Concern
- 1
attr_reader :migration_number, :migration_file_name, :migration_class_name
- 1
module ClassMethods #:nodoc:
- 1
def migration_lookup_at(dirname)
Dir.glob("#{dirname}/[0-9]*_*.rb")
end
- 1
def migration_exists?(dirname, file_name)
migration_lookup_at(dirname).grep(/\d+_#{file_name}.rb$/).first
end
- 1
def current_migration_number(dirname)
migration_lookup_at(dirname).collect do |file|
File.basename(file).split("_").first.to_i
end.max.to_i
end
- 1
def next_migration_number(dirname)
raise NotImplementedError
end
end
- 1
def create_migration(destination, data, config = {}, &block)
action Rails::Generators::Actions::CreateMigration.new(self, destination, block || data.to_s, config)
end
- 1
def set_migration_assigns!(destination)
destination = File.expand_path(destination, 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"
- 1
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("_"))
file = create_migration numbered_destination, nil, config do
if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
ERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer").result(context)
else
ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
end
end
Rails::Generators.add_generated_file(file)
end
end
end
end
# frozen_string_literal: true
- 3
require "rails/generators/active_model"
- 3
module Rails
- 3
module Generators
- 3
module ModelHelpers # :nodoc:
- 3
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."
- 3
IRREGULAR_MODEL_NAME_WARN_MESSAGE = <<~WARNING
[WARNING] Rails cannot recover singular form from its plural form '%s'.
Please setup custom inflection rules for this noun before running the generator in config/initializers/inflections.rb.
WARNING
- 3
mattr_accessor :skip_warn
- 3
def self.included(base) #:nodoc:
- 4
base.class_option :force_plural, type: :boolean, default: false, desc: "Forces the use of the given model name"
end
- 3
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]
end
name.replace singular
assign_names!(name)
end
if name.singularize != name.pluralize.singularize && ! ModelHelpers.skip_warn
say IRREGULAR_MODEL_NAME_WARN_MESSAGE % [name.pluralize]
end
ModelHelpers.skip_warn = true
end
end
end
end
# frozen_string_literal: true
- 13
require "rails/generators/base"
- 13
require "rails/generators/generated_attribute"
- 13
module Rails
- 13
module Generators
- 13
class NamedBase < Base
- 13
argument :name, type: :string
- 13
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!(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.
- 13
no_tasks do
- 13
def template(source, *args, &block)
inside_template do
Rails::Generators.add_generated_file(super)
end
end
- 13
def js_template(source, destination)
template(source + ".js", destination + ".js")
end
end
- 13
private
- 13
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.
- 13
def singular_name # :doc:
file_name
end
- 13
def inside_template # :doc:
@inside_template = true
yield
ensure
@inside_template = false
end
- 13
def inside_template? # :doc:
@inside_template
end
- 13
def file_path # :doc:
@file_path ||= (class_path + [file_name]).join("/")
end
- 13
def class_path # :doc:
inside_template? || !namespaced? ? regular_class_path : namespaced_class_path
end
- 13
def regular_class_path # :doc:
@class_path
end
- 13
def namespaced_class_path # :doc:
@namespaced_class_path ||= namespace_dirs + @class_path
end
- 13
def class_name # :doc:
(class_path + [file_name]).map!(&:camelize).join("::")
end
- 13
def human_name # :doc:
@human_name ||= singular_name.humanize
end
- 13
def plural_name # :doc:
@plural_name ||= singular_name.pluralize
end
- 13
def i18n_scope # :doc:
@i18n_scope ||= file_path.tr("/", ".")
end
- 13
def table_name # :doc:
@table_name ||= begin
base = pluralize_table_names? ? plural_name : singular_name
(class_path + [base]).join("_")
end
end
- 13
def uncountable? # :doc:
singular_name == plural_name
end
- 13
def index_helper # :doc:
uncountable? ? "#{plural_route_name}_index" : plural_route_name
end
- 13
def show_helper # :doc:
"#{singular_route_name}_url(@#{singular_table_name})"
end
- 13
def edit_helper # :doc:
"edit_#{show_helper}"
end
- 13
def new_helper # :doc:
"new_#{singular_route_name}_url"
end
- 13
def singular_table_name # :doc:
@singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name)
end
- 13
def plural_table_name # :doc:
@plural_table_name ||= (pluralize_table_names? ? table_name : table_name.pluralize)
end
- 13
def plural_file_name # :doc:
@plural_file_name ||= file_name.pluralize
end
- 13
def fixture_file_name # :doc:
@fixture_file_name ||= (pluralize_table_names? ? plural_file_name : file_name)
end
- 13
def route_url # :doc:
@route_url ||= class_path.collect { |dname| "/" + dname }.join + "/" + plural_file_name
end
- 13
def url_helper_prefix # :doc:
@url_helper_prefix ||= (class_path + [file_name]).join("_")
end
# Tries to retrieve the application name or simply return application.
- 13
def application_name # :doc:
if defined?(Rails) && Rails.application
Rails.application.class.name.split("::").first.underscore
else
"application"
end
end
- 13
def redirect_resource_name # :doc:
model_resource_name(prefix: "@")
end
- 13
def model_resource_name(prefix: "") # :doc:
resource_name = "#{prefix}#{singular_table_name}"
if options[:model_name]
"[#{controller_class_path.map { |name| ":" + name }.join(", ")}, #{resource_name}]"
else
resource_name
end
end
- 13
def singular_route_name # :doc:
if options[:model_name]
"#{controller_class_path.join('_')}_#{singular_table_name}"
else
singular_table_name
end
end
- 13
def plural_route_name # :doc:
if options[:model_name]
"#{controller_class_path.join('_')}_#{plural_table_name}"
else
plural_table_name
end
end
- 13
def assign_names!(name)
@class_path = name.include?("/") ? name.split("/") : name.split("::")
@class_path.map!(&:underscore)
@file_name = @class_path.pop
end
# Convert attributes array into GeneratedAttribute objects.
- 13
def parse_attributes!
self.attributes = (attributes || []).map do |attr|
Rails::Generators::GeneratedAttribute.parse(attr)
end
end
- 13
def attributes_names # :doc:
@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
- 13
def pluralize_table_names? # :doc:
!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names
end
- 13
def mountable_engine? # :doc:
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".
#
- 13
def self.check_class_collision(options = {}) # :doc:
- 8
define_method :check_class_collision do
name = if respond_to?(:controller_class_name, true) # for ResourceHelpers
controller_class_name
else
class_name
end
class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}"
end
end
end
end
end
# frozen_string_literal: true
- 3
require "rails/generators/app_base"
- 3
module Rails
- 3
module ActionMethods # :nodoc:
- 3
attr_reader :options
- 3
def initialize(generator)
@generator = generator
@options = generator.options
end
- 3
private
%w(template copy_file directory empty_directory inside
- 3
empty_directory_with_keep_file create_file chmod shebang).each do |method|
- 27
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
@generator.send(:#{method}, *args, &block)
end
RUBY
end
- 3
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 CustomAppBuilder < Rails::AppBuilder
# def test
# @generator.gem "rspec-rails", group: [:development, :test]
# run "bundle install"
# generate "rspec:install"
# end
# end
- 3
class AppBuilder
- 3
def rakefile
template "Rakefile"
end
- 3
def readme
copy_file "README.md", "README.md"
end
- 3
def ruby_version
template "ruby-version", ".ruby-version"
end
- 3
def gemfile
template "Gemfile"
end
- 3
def configru
template "config.ru"
end
- 3
def gitignore
template "gitignore", ".gitignore"
end
- 3
def version_control
if !options[:skip_git] && !options[:pretend]
run "git init", capture: options[:quiet], abort_on_failure: false
end
end
- 3
def package_json
template "package.json"
end
- 3
def app
directory "app"
empty_directory_with_keep_file "app/assets/images"
keep_file "app/controllers/concerns"
keep_file "app/models/concerns"
end
- 3
def bin
directory "bin" do |content|
"#{shebang}\n" + content
end
chmod "bin", 0755 & ~File.umask, verbose: false
end
- 3
def bin_when_updating
bin
if options[:skip_javascript]
remove_file "bin/yarn"
end
end
- 3
def yarn_when_updating
return if File.exist?("bin/yarn")
template "bin/yarn" do |content|
"#{shebang}\n" + content
end
chmod "bin", 0755 & ~File.umask, verbose: false
end
- 3
def config
empty_directory "config"
inside "config" do
template "routes.rb"
template "application.rb"
template "environment.rb"
template "cable.yml" unless options[:skip_action_cable]
template "puma.rb" unless options[:skip_puma]
template "spring.rb" if spring_install?
template "storage.yml" unless skip_active_storage?
directory "environments"
directory "initializers"
directory "locales"
end
end
- 3
def config_when_updating
cookie_serializer_config_exist = File.exist?("config/initializers/cookies_serializer.rb")
action_cable_config_exist = File.exist?("config/cable.yml")
active_storage_config_exist = File.exist?("config/storage.yml")
rack_cors_config_exist = File.exist?("config/initializers/cors.rb")
assets_config_exist = File.exist?("config/initializers/assets.rb")
csp_config_exist = File.exist?("config/initializers/content_security_policy.rb")
feature_policy_config_exist = File.exist?("config/initializers/feature_policy.rb")
@config_target_version = Rails.application.config.loaded_config_version || "5.0"
config
unless cookie_serializer_config_exist
gsub_file "config/initializers/cookies_serializer.rb", /json(?!,)/, "marshal"
end
if !options[:skip_action_cable] && !action_cable_config_exist
template "config/cable.yml"
end
if !skip_active_storage? && !active_storage_config_exist
template "config/storage.yml"
end
if options[:skip_sprockets] && !assets_config_exist
remove_file "config/initializers/assets.rb"
end
unless rack_cors_config_exist
remove_file "config/initializers/cors.rb"
end
if options[:api]
unless cookie_serializer_config_exist
remove_file "config/initializers/cookies_serializer.rb"
end
unless csp_config_exist
remove_file "config/initializers/content_security_policy.rb"
end
unless feature_policy_config_exist
remove_file "config/initializers/feature_policy.rb"
end
end
end
- 3
def master_key
return if options[:pretend] || options[:dummy_app]
require "rails/generators/rails/master_key/master_key_generator"
master_key_generator = Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet], force: options[:force])
master_key_generator.add_master_key_file_silently
master_key_generator.ignore_master_key_file_silently
end
- 3
def credentials
return if options[:pretend] || options[:dummy_app]
require "rails/generators/rails/credentials/credentials_generator"
Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently
end
- 3
def database_yml
template "config/databases/#{options[:database]}.yml", "config/database.yml"
end
- 3
def db
directory "db"
end
- 3
def lib
empty_directory "lib"
empty_directory_with_keep_file "lib/tasks"
empty_directory_with_keep_file "lib/assets"
end
- 3
def log
empty_directory_with_keep_file "log"
end
- 3
def public_directory
directory "public", "public", recursive: false
end
- 3
def storage
empty_directory_with_keep_file "storage"
empty_directory_with_keep_file "tmp/storage"
end
- 3
def test
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/channels/application_cable/connection_test.rb"
template "test/test_helper.rb"
end
- 3
def system_test
empty_directory_with_keep_file "test/system"
template "test/application_system_test_case.rb"
end
- 3
def tmp
empty_directory_with_keep_file "tmp"
empty_directory_with_keep_file "tmp/pids"
empty_directory "tmp/cache"
empty_directory "tmp/cache/assets"
end
- 3
def vendor
empty_directory_with_keep_file "vendor"
end
- 3
def config_target_version
defined?(@config_target_version) ? @config_target_version : Rails::VERSION::STRING.to_f
end
end
- 3
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.
- 3
RAILS_DEV_PATH = File.expand_path("../../../../../..", __dir__)
- 3
class AppGenerator < AppBase
# :stopdoc:
- 3
WEBPACKS = %w( react vue angular elm stimulus )
- 3
add_shared_options_for "application"
# Add rails command options
- 3
class_option :version, type: :boolean, aliases: "-v", group: :rails,
desc: "Show Rails version number and quit"
- 3
class_option :api, type: :boolean,
desc: "Preconfigure smaller stack for API only apps"
- 3
class_option :minimal, type: :boolean,
desc: "Preconfigure a minimal rails app"
- 3
class_option :skip_bundle, type: :boolean, aliases: "-B", default: false,
desc: "Don't run bundle install"
- 3
class_option :webpack, type: :string, aliases: "--webpacker", default: nil,
desc: "Preconfigure Webpack with a particular framework (options: #{WEBPACKS.join(", ")})"
- 3
class_option :skip_webpack_install, type: :boolean, default: false,
desc: "Don't run Webpack install"
- 3
def initialize(*args)
super
if !options[:skip_active_record] && !DATABASES.include?(options[:database])
raise Error, "Invalid value for --database option. Supported preconfigurations are: #{DATABASES.join(", ")}."
end
# Force sprockets and yarn to be skipped when generating API only apps.
# Can't modify options hash as it's frozen by default.
if options[:api]
self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze
end
if options[:minimal]
self.options = options.merge(
skip_action_cable: true,
skip_action_mailer: true,
skip_action_mailbox: true,
skip_action_text: true,
skip_active_job: true,
skip_active_storage: true,
skip_bootsnap: true,
skip_dev_gems: true,
skip_javascript: true,
skip_jbuilder: true,
skip_spring: true,
skip_system_test: true,
skip_webpack_install: true,
skip_turbolinks: true).tap do |option|
if option[:webpack]
option[:skip_webpack_install] = false
option[:skip_javascript] = false
end
end.freeze
end
@after_bundle_callbacks = []
end
- 3
public_task :set_default_accessors!
- 3
public_task :create_root
- 3
def create_root_files
build(:readme)
build(:rakefile)
build(:ruby_version)
build(:configru)
build(:gitignore) unless options[:skip_git]
build(:gemfile) unless options[:skip_gemfile]
build(:version_control)
build(:package_json) unless options[:skip_javascript]
end
- 3
def create_app_files
build(:app)
end
- 3
def create_bin_files
build(:bin)
end
- 3
def update_bin_files
build(:bin_when_updating)
end
- 3
remove_task :update_bin_files
- 3
def update_bin_yarn
build(:yarn_when_updating)
end
- 3
remove_task :update_bin_yarn
- 3
def update_active_storage
unless skip_active_storage?
rails_command "active_storage:update", inline: true
end
end
- 3
remove_task :update_active_storage
- 3
def create_config_files
build(:config)
end
- 3
def update_config_files
build(:config_when_updating)
end
- 3
remove_task :update_config_files
- 3
def create_master_key
build(:master_key)
end
- 3
def create_credentials
build(:credentials)
end
- 3
def display_upgrade_guide_info
say "\nAfter this, check Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html for more details about upgrading your app."
end
- 3
remove_task :display_upgrade_guide_info
- 3
def create_boot_file
template "config/boot.rb"
end
- 3
def create_active_record_files
return if options[:skip_active_record]
build(:database_yml)
end
- 3
def create_db_files
return if options[:skip_active_record]
build(:db)
end
- 3
def create_lib_files
build(:lib)
end
- 3
def create_log_files
build(:log)
end
- 3
def create_public_files
build(:public_directory)
end
- 3
def create_tmp_files
build(:tmp)
end
- 3
def create_vendor_files
build(:vendor)
end
- 3
def create_test_files
build(:test) unless options[:skip_test]
end
- 3
def create_system_test_files
build(:system_test) if depends_on_system_test?
end
- 3
def create_storage_files
build(:storage) unless skip_active_storage?
end
- 3
def delete_app_assets_if_api_option
if options[:api]
remove_dir "app/assets"
remove_dir "lib/assets"
remove_dir "tmp/cache/assets"
end
end
- 3
def delete_app_helpers_if_api_option
if options[:api]
remove_dir "app/helpers"
remove_dir "test/helpers"
end
end
- 3
def delete_app_views_if_api_option
if options[:api]
if options[:skip_action_mailer]
remove_dir "app/views"
else
remove_file "app/views/layouts/application.html.erb"
end
end
end
- 3
def delete_public_files_if_api_option
if options[:api]
remove_file "public/404.html"
remove_file "public/422.html"
remove_file "public/500.html"
remove_file "public/apple-touch-icon-precomposed.png"
remove_file "public/apple-touch-icon.png"
remove_file "public/favicon.ico"
end
end
- 3
def delete_js_folder_skipping_javascript
if options[:skip_javascript] && !options[:minimal]
remove_dir "app/javascript"
end
end
- 3
def delete_js_packs_when_minimal_skipping_webpack
if options[:minimal] && options[:skip_webpack_install]
remove_dir "app/javascript/packs"
keep_file "app/javascript"
end
end
- 3
def delete_assets_initializer_skipping_sprockets
if options[:skip_sprockets]
remove_file "config/initializers/assets.rb"
end
end
- 3
def delete_application_record_skipping_active_record
if options[:skip_active_record]
remove_file "app/models/application_record.rb"
end
end
- 3
def delete_active_job_folder_if_skipping_active_job
if options[:skip_active_job]
remove_dir "app/jobs"
end
end
- 3
def delete_action_mailer_files_skipping_action_mailer
if options[:skip_action_mailer]
remove_file "app/views/layouts/mailer.html.erb"
remove_file "app/views/layouts/mailer.text.erb"
remove_dir "app/mailers"
remove_dir "test/mailers"
end
end
- 3
def delete_action_cable_files_skipping_action_cable
if options[:skip_action_cable]
remove_dir "app/javascript/channels"
remove_dir "app/channels"
remove_dir "test/channels"
end
end
- 3
def delete_non_api_initializers_if_api_option
if options[:api]
remove_file "config/initializers/cookies_serializer.rb"
remove_file "config/initializers/content_security_policy.rb"
remove_file "config/initializers/feature_policy.rb"
end
end
- 3
def delete_api_initializers
unless options[:api]
remove_file "config/initializers/cors.rb"
end
end
- 3
def delete_new_framework_defaults
unless options[:update]
remove_file "config/initializers/new_framework_defaults_6_1.rb"
end
end
- 3
def delete_bin_yarn
remove_file "bin/yarn" if options[:skip_javascript]
end
- 3
def finish_template
build(:leftovers)
end
- 3
public_task :apply_rails_template, :run_bundle
- 3
public_task :generate_bundler_binstub, :generate_spring_binstub
- 3
public_task :run_webpack
- 3
def run_after_bundle_callbacks
@after_bundle_callbacks.each(&:call)
end
- 3
def self.banner
"rails new #{arguments.map(&:usage).join(' ')} [options]"
end
# :startdoc:
- 3
private
# Define file as an alias to create_file for backwards compatibility.
- 3
def file(*args, &block)
create_file(*args, &block)
end
# Registers a callback to be executed after bundle and spring binstubs
# have run.
#
# after_bundle do
# git add: '.'
# end
- 3
def after_bundle(&block) # :doc:
@after_bundle_callbacks << block
end
- 3
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.
- 3
class ARGVScrubber # :nodoc:
- 3
def initialize(argv = ARGV)
@argv = argv
end
- 3
def prepare!
handle_version_request!(@argv.first)
handle_invalid_command!(@argv.first, @argv) do
handle_rails_rc!(@argv.drop(1))
end
end
- 3
def self.default_rc_file
File.expand_path("~/.railsrc")
end
- 3
private
- 3
def handle_version_request!(argument)
if ["--version", "-v"].include?(argument)
require "rails/version"
puts "Rails #{Rails::VERSION::STRING}"
exit(0)
end
end
- 3
def handle_invalid_command!(argument, argv)
if argument == "new"
yield
else
["--help"] + argv.drop(1)
end
end
- 3
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
- 3
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
- 3
def read_rc_file(railsrc)
extra_args = File.readlines(railsrc).flat_map(&:split)
puts "Using #{extra_args.join(" ")} from #{railsrc}"
extra_args
end
- 3
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
# frozen_string_literal: true
- 1
module Rails
- 1
module Generators
- 1
class ApplicationRecordGenerator < Base # :nodoc:
- 1
hook_for :orm, required: true, desc: "ORM to be invoked"
end
end
end
# frozen_string_literal: true
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"
private
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
# frozen_string_literal: true
require "rails/generators/named_base"
module Rails
module Generators
class BenchmarkGenerator < NamedBase
IPS_GEM_NAME = "benchmark-ips"
argument :reports, type: :array, default: ["before", "after"]
def generate_layout
add_ips_to_gemfile unless ips_installed?
template("benchmark.rb.tt", "script/benchmarks/#{file_name}.rb")
end
private
def add_ips_to_gemfile
gem(IPS_GEM_NAME, group: [:development, :test])
end
def ips_installed?
in_root do
return File.read("Gemfile").match?(/gem.*\b#{IPS_GEM_NAME}\b.*/)
end
end
end
end
end
# frozen_string_literal: true
- 2
module Rails
- 2
module Generators
- 2
class ControllerGenerator < NamedBase # :nodoc:
- 2
argument :actions, type: :array, default: [], banner: "action action"
- 2
class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
- 2
class_option :helper, type: :boolean
- 2
class_option :assets, type: :boolean
- 2
check_class_collision suffix: "Controller"
- 2
def create_controller_files
template "controller.rb", File.join("app/controllers", class_path, "#{file_name}_controller.rb")
end
- 2
def add_routes
return if options[:skip_routes]
return if actions.empty?
routing_code = actions.map { |action| "get '#{file_name}/#{action}'" }.join("\n")
route routing_code, namespace: regular_class_path
end
- 2
hook_for :template_engine, :test_framework, :helper, :assets do |generator|
invoke generator, [ remove_possible_suffix(name), actions ]
end
- 2
private
- 2
def file_name
@_file_name ||= remove_possible_suffix(super)
end
- 2
def remove_possible_suffix(name)
name.sub(/_?controller$/i, "")
end
end
end
end
# frozen_string_literal: true
require "rails/generators/base"
require "rails/generators/rails/master_key/master_key_generator"
require "active_support/encrypted_configuration"
module Rails
module Generators
class CredentialsGenerator < Base # :nodoc:
def add_credentials_file
unless credentials.content_path.exist?
template = credentials_template
say "Adding #{credentials.content_path} to store encrypted credentials."
say ""
say "The following content has been encrypted with the Rails master key:"
say ""
say template, :on_green
say ""
add_credentials_file_silently(template)
say "You can edit encrypted credentials with `bin/rails credentials:edit`."
say ""
end
end
def add_credentials_file_silently(template = nil)
unless credentials.content_path.exist?
credentials.write(credentials_template)
end
end
private
def credentials
ActiveSupport::EncryptedConfiguration.new(
config_path: "config/credentials.yml.enc",
key_path: "config/master.key",
env_key: "RAILS_MASTER_KEY",
raise_if_missing_key: true
)
end
def credentials_template
<<~YAML
# aws:
# access_key_id: 123
# secret_access_key: 345
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: #{SecureRandom.hex(64)}
YAML
end
end
end
end
# frozen_string_literal: true
require "rails/generators/base"
module Rails
module Generators
module Db
module System
class ChangeGenerator < Base # :nodoc:
include Database
include AppName
class_option :to, required: true,
desc: "The database system to switch to."
def self.default_generator_root
path = File.expand_path(File.join(base_name, "app"), base_root)
path if File.exist?(path)
end
def initialize(*)
super
unless DATABASES.include?(options[:to])
raise Error, "Invalid value for --to option. Supported preconfigurations are: #{DATABASES.join(", ")}."
end
opt = options.dup
opt[:database] ||= opt[:to]
self.options = opt.freeze
end
def edit_database_config
template("config/databases/#{options[:database]}.yml", "config/database.yml")
end
def edit_gemfile
name, version = gem_for_database
gsub_file("Gemfile", all_database_gems_regex, name)
gsub_file("Gemfile", gem_entry_regex_for(name), gem_entry_for(name, *version))
end
private
def all_database_gems
DATABASES.map { |database| gem_for_database(database) }
end
def all_database_gems_regex
all_database_gem_names = all_database_gems.map(&:first)
/(\b#{all_database_gem_names.join('\b|\b')}\b)/
end
def gem_entry_regex_for(gem_name)
/^gem.*\b#{gem_name}\b.*/
end
def gem_entry_for(*gem_name_and_version)
gem_name_and_version.map! { |segment| "'#{segment}'" }
"gem #{gem_name_and_version.join(", ")}"
end
end
end
end
end
end
# frozen_string_literal: true
require "rails/generators/base"
require "active_support/encrypted_file"
module Rails
module Generators
class EncryptedFileGenerator < Base # :nodoc:
def add_encrypted_file_silently(file_path, key_path, template = encrypted_file_template)
unless File.exist?(file_path)
ActiveSupport::EncryptedFile.new(
content_path: file_path,
key_path: key_path,
env_key: "RAILS_MASTER_KEY",
raise_if_missing_key: true
).write(template)
end
end
private
def encrypted_file_template
<<~YAML
# aws:
# access_key_id: 123
# secret_access_key: 345
YAML
end
end
end
end
# frozen_string_literal: true
require "pathname"
require "rails/generators/base"
require "active_support/encrypted_file"
module Rails
module Generators
class EncryptionKeyFileGenerator < Base # :nodoc:
def add_key_file(key_path)
key_path = Pathname.new(key_path)
unless key_path.exist?
key = ActiveSupport::EncryptedFile.generate_key
log "Adding #{key_path} to store the encryption key: #{key}"
log ""
log "Save this in a password manager your team can access."
log ""
log "If you lose the key, no one, including you, can access anything encrypted with it."
log ""
add_key_file_silently(key_path, key)
log ""
end
end
def add_key_file_silently(key_path, key = nil)
create_file key_path, key || ActiveSupport::EncryptedFile.generate_key
key_path.chmod 0600
end
def ignore_key_file(key_path, ignore: key_ignore(key_path))
if File.exist?(".gitignore")
unless File.read(".gitignore").include?(ignore)
log "Ignoring #{key_path} so it won't end up in Git history:"
log ""
append_to_file ".gitignore", ignore
log ""
end
else
log "IMPORTANT: Don't commit #{key_path}. Add this to your ignore file:"
log ignore, :on_green
log ""
end
end
def ignore_key_file_silently(key_path, ignore: key_ignore(key_path))
append_to_file ".gitignore", ignore if File.exist?(".gitignore")
end
private
def key_ignore(key_path)
[ "", "/#{key_path}", "" ].join("\n")
end
end
end
end
# frozen_string_literal: true
- 1
module Rails
- 1
module Generators
- 1
class GeneratorGenerator < NamedBase # :nodoc:
- 1
check_class_collision suffix: "Generator"
- 1
class_option :namespace, type: :boolean, default: true,
desc: "Namespace generator under lib/generators/name"
- 1
def create_generator_files
directory ".", generator_dir
end
- 1
hook_for :test_framework
- 1
private
- 1
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
# frozen_string_literal: true
- 1
module Rails
- 1
module Generators
- 1
class HelperGenerator < NamedBase # :nodoc:
- 1
check_class_collision suffix: "Helper"
- 1
def create_helper_files
template "helper.rb", File.join("app/helpers", class_path, "#{file_name}_helper.rb")
end
- 1
hook_for :test_framework
- 1
private
- 1
def file_name
@_file_name ||= super.sub(/_helper\z/i, "")
end
end
end
end
# frozen_string_literal: true
- 1
module Rails
- 1
module Generators
- 1
class IntegrationTestGenerator < NamedBase # :nodoc:
- 1
hook_for :integration_tool, as: :integration
end
end
end
# frozen_string_literal: true
require "pathname"
require "rails/generators/base"
require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
require "active_support/encrypted_file"
module Rails
module Generators
class MasterKeyGenerator < Base # :nodoc:
MASTER_KEY_PATH = Pathname.new("config/master.key")
def add_master_key_file
unless MASTER_KEY_PATH.exist?
key = ActiveSupport::EncryptedFile.generate_key
log "Adding #{MASTER_KEY_PATH} to store the master encryption key: #{key}"
log ""
log "Save this in a password manager your team can access."
log ""
log "If you lose the key, no one, including you, can access anything encrypted with it."
log ""
add_master_key_file_silently(key)
log ""
end
end
def add_master_key_file_silently(key = nil)
unless MASTER_KEY_PATH.exist?
key_file_generator.add_key_file_silently(MASTER_KEY_PATH, key)
end
end
def ignore_master_key_file
key_file_generator.ignore_key_file(MASTER_KEY_PATH, ignore: key_ignore)
end
def ignore_master_key_file_silently
key_file_generator.ignore_key_file_silently(MASTER_KEY_PATH, ignore: key_ignore)
end
private
def key_file_generator
EncryptionKeyFileGenerator.new([], options)
end
def key_ignore
[ "", "# Ignore master key for decrypting credentials and more.", "/#{MASTER_KEY_PATH}", "" ].join("\n")
end
end
end
end
# frozen_string_literal: true
- 2
module Rails
- 2
module Generators
- 2
class MigrationGenerator < NamedBase # :nodoc:
- 2
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
- 2
hook_for :orm, required: true, desc: "ORM to be invoked"
end
end
end
# frozen_string_literal: true
- 2
require "rails/generators/model_helpers"
- 2
module Rails
- 2
module Generators
- 2
class ModelGenerator < NamedBase # :nodoc:
- 2
include Rails::Generators::ModelHelpers
- 2
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
- 2
hook_for :orm, required: true, desc: "ORM to be invoked"
end
end
end
# frozen_string_literal: true
- 1
require "active_support/core_ext/hash/except"
- 1
require "rails/generators/rails/app/app_generator"
- 1
require "date"
- 1
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.
- 1
class PluginBuilder
- 1
def rakefile
template "Rakefile"
end
- 1
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
remove_dir "app/mailers" if options[:skip_action_mailer]
remove_dir "app/jobs" if options[:skip_active_job]
elsif full?
empty_directory_with_keep_file "app/models"
empty_directory_with_keep_file "app/controllers"
empty_directory_with_keep_file "app/mailers" unless options[:skip_action_mailer]
empty_directory_with_keep_file "app/jobs" unless options[:skip_active_job]
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
- 1
def readme
template "README.md"
end
- 1
def gemfile
template "Gemfile"
end
- 1
def license
template "MIT-LICENSE"
end
- 1
def gemspec
template "%name%.gemspec"
end
- 1
def gitignore
template "gitignore", ".gitignore"
end
- 1
def version_control
if !options[:skip_git] && !options[:pretend]
run "git init", capture: options[:quiet], abort_on_failure: false
end
end
- 1
def lib
template "lib/%namespaced_name%.rb"
template "lib/tasks/%namespaced_name%_tasks.rake"
template "lib/%namespaced_name%/version.rb"
if engine?
template "lib/%namespaced_name%/engine.rb"
else
template "lib/%namespaced_name%/railtie.rb"
end
end
- 1
def config
template "config/routes.rb" if engine?
end
- 1
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
- 1
DUMMY_IGNORE_OPTIONS = %i[dev edge master template]
- 1
def generate_test_dummy(force = false)
opts = options.transform_keys(&:to_sym).except(*DUMMY_IGNORE_OPTIONS)
opts[:force] = force
opts[:skip_bundle] = true
opts[:skip_listen] = true
opts[:skip_git] = true
opts[:skip_turbolinks] = true
opts[:skip_webpack_install] = true
opts[:dummy_app] = true
invoke Rails::Generators::AppGenerator,
[ File.expand_path(dummy_path, destination_root) ], opts
end
- 1
def test_dummy_config
template "rails/boot.rb", "#{dummy_path}/config/boot.rb", force: true
insert_into_file "#{dummy_path}/config/application.rb", <<~RUBY, after: /^Bundler\.require.+\n/
require #{namespaced_name.inspect}
RUBY
if mountable?
template "rails/routes.rb", "#{dummy_path}/config/routes.rb", force: true
end
end
- 1
def test_dummy_assets
template "rails/javascripts.js", "#{dummy_path}/app/javascript/packs/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
- 1
def test_dummy_clean
inside dummy_path do
remove_file ".ruby-version"
remove_file "db/seeds.rb"
remove_file "Gemfile"
remove_file "lib/tasks"
remove_file "public/robots.txt"
remove_file "README.md"
remove_file "test"
remove_file "vendor"
end
end
- 1
def assets_manifest
template "rails/engine_manifest.js", "app/assets/config/#{underscored_name}_manifest.js"
end
- 1
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
- 1
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
- 1
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 = "\ngem '#{name}', path: '#{relative_path}'"
append_file gemfile_in_app_path, entry
end
end
end
- 1
module Generators
- 1
class PluginGenerator < AppBase # :nodoc:
- 1
add_shared_options_for "plugin"
- 1
alias_method :plugin_path, :app_path
- 1
class_option :dummy_path, type: :string, default: "test/dummy",
desc: "Create dummy application at given path"
- 1
class_option :full, type: :boolean, default: false,
desc: "Generate a rails engine with bundled Rails application for testing"
- 1
class_option :mountable, type: :boolean, default: false,
desc: "Generate mountable isolated engine"
- 1
class_option :skip_gemspec, type: :boolean, default: false,
desc: "Skip gemspec file"
- 1
class_option :skip_gemfile_entry, type: :boolean, default: false,
desc: "If creating plugin in application's directory " \
"skip adding entry to Gemfile"
- 1
class_option :api, type: :boolean, default: false,
desc: "Generate a smaller stack for API application plugins"
- 1
def initialize(*args)
@dummy_path = nil
super
end
- 1
public_task :set_default_accessors!
- 1
public_task :create_root
- 1
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]
build(:version_control)
end
- 1
def create_app_files
build(:app)
end
- 1
def create_config_files
build(:config)
end
- 1
def create_lib_files
build(:lib)
end
- 1
def create_assets_manifest_file
build(:assets_manifest) if !api? && engine?
end
- 1
def create_public_stylesheets_files
build(:stylesheets) unless api?
end
- 1
def create_bin_files
build(:bin)
end
- 1
def create_test_files
build(:test) unless options[:skip_test]
end
- 1
def create_test_dummy_files
return unless with_dummy_app?
create_dummy_app
end
- 1
def update_gemfile
build(:gemfile_entry) unless options[:skip_gemfile_entry]
end
- 1
def finish_template
build(:leftovers)
end
- 1
public_task :apply_rails_template
- 1
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
- 1
def underscored_name
@underscored_name ||= original_name.underscore
end
- 1
def namespaced_name
@namespaced_name ||= name.tr("-", "/")
end
- 1
private
- 1
def create_dummy_app(path = nil)
dummy_path(path) if path
say_status :vendor_app, dummy_path
mute do
build(:generate_test_dummy)
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
- 1
def engine?
full? || mountable? || options[:engine]
end
- 1
def full?
options[:full]
end
- 1
def mountable?
options[:mountable]
end
- 1
def skip_git?
options[:skip_git]
end
- 1
def with_dummy_app?
options[:skip_test].blank? || options[:dummy_path] != "test/dummy"
end
- 1
def api?
options[:api]
end
- 1
def self.banner
"rails plugin new #{arguments.map(&:usage).join(' ')} [options]"
end
- 1
def original_name
@original_name ||= File.basename(destination_root)
end
- 1
def modules
@modules ||= namespaced_name.camelize.split("::")
end
- 1
def wrap_in_modules(unwrapped_code)
unwrapped_code = "#{unwrapped_code}".strip.gsub(/\s$\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
- 1
def camelized_modules
@camelized_modules ||= namespaced_name.camelize
end
- 1
def humanized
@humanized ||= original_name.underscore.humanize
end
- 1
def camelized
@camelized ||= name.gsub(/\W/, "_").squeeze("_").camelize
end
- 1
def author
default = "TODO: Write your name"
if skip_git?
@author = default
else
@author = `git config user.name`.chomp rescue default
end
end
- 1
def email
default = "TODO: Write your email address"
if skip_git?
@email = default
else
@email = `git config user.email`.chomp rescue default
end
end
- 1
def valid_const?
if /-\d/.match?(original_name)
raise Error, "Invalid plugin name #{original_name}. Please give a name which does not contain a namespace starting with numeric characters."
elsif /[^\w-]+/.match?(original_name)
raise Error, "Invalid plugin name #{original_name}. Please give a name which uses only alphabetic, numeric, \"_\" or \"-\" characters."
elsif /^\d/.match?(camelized)
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
- 1
def get_builder_class
defined?(::PluginBuilder) ? ::PluginBuilder : Rails::PluginBuilder
end
- 1
def rakefile_test_tasks
<<-RUBY
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end
RUBY
end
- 1
def dummy_path(path = nil)
@dummy_path = path if path
@dummy_path || options[:dummy_path]
end
- 1
def mute(&block)
shell.mute(&block)
end
- 1
def rails_app_path
APP_PATH.sub("/config/application", "") if defined?(APP_PATH)
end
- 1
def inside_application?
rails_app_path && destination_root.start_with?(rails_app_path.to_s)
end
- 1
def relative_path
return unless inside_application?
app_path.delete_prefix("#{rails_app_path}/")
end
end
end
end
# frozen_string_literal: true
- 1
require "rails/generators/resource_helpers"
- 1
require "rails/generators/rails/model/model_generator"
- 1
module Rails
- 1
module Generators
- 1
class ResourceGenerator < ModelGenerator # :nodoc:
- 1
include ResourceHelpers
- 1
hook_for :resource_controller, required: true do |controller|
invoke controller, [ controller_name, options[:actions] ]
end
- 1
class_option :actions, type: :array, banner: "ACTION ACTION", default: [],
desc: "Actions for the resource controller"
- 1
hook_for :resource_route, required: true
end
end
end
# frozen_string_literal: true
module Rails
module Generators
class ResourceRouteGenerator < NamedBase # :nodoc:
# Properly nests namespaces passed into a generator
#
# $ bin/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?
route "resources :#{file_name.pluralize}", namespace: regular_class_path
end
end
end
end
# frozen_string_literal: true
- 1
require "rails/generators/rails/resource/resource_generator"
- 1
module Rails
- 1
module Generators
- 1
class ScaffoldGenerator < ResourceGenerator # :nodoc:
- 1
remove_hook_for :resource_controller
- 1
remove_class_option :actions
- 1
class_option :api, type: :boolean
- 1
class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
- 1
class_option :stylesheet_engine, desc: "Engine for Stylesheets"
- 1
class_option :assets, type: :boolean
- 1
class_option :resource_route, type: :boolean
- 1
class_option :scaffold_stylesheet, type: :boolean
- 1
def handle_skip
@options = @options.merge(stylesheets: false) unless options[:assets]
@options = @options.merge(stylesheet_engine: false) unless options[:stylesheets] && options[:scaffold_stylesheet]
end
- 1
hook_for :scaffold_controller, required: true
- 1
hook_for :assets do |assets|
invoke assets, [controller_name]
end
- 1
hook_for :stylesheet_engine do |stylesheet_engine|
if behavior == :invoke
invoke stylesheet_engine, [controller_name]
end
end
end
end
end
# frozen_string_literal: true
- 1
require "rails/generators/resource_helpers"
- 1
module Rails
- 1
module Generators
- 1
class ScaffoldControllerGenerator < NamedBase # :nodoc:
- 1
include ResourceHelpers
- 1
check_class_collision suffix: "Controller"
- 1
class_option :helper, type: :boolean
- 1
class_option :orm, banner: "NAME", type: :string, required: true,
desc: "ORM to generate the controller for"
- 1
class_option :api, type: :boolean,
desc: "Generates API controller"
- 1
class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
- 1
argument :attributes, type: :array, default: [], banner: "field:type field:type"
- 1
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
- 1
hook_for :template_engine, as: :scaffold do |template_engine|
invoke template_engine unless options.api?
end
- 1
hook_for :resource_route, required: true do |route|
invoke route unless options.skip_routes?
end
- 1
hook_for :test_framework, as: :scaffold
# Invoke the helper using the controller name (pluralized)
- 1
hook_for :helper, as: :scaffold do |invoked|
invoke invoked, [ controller_name ]
end
- 1
private
- 1
def permitted_params
attachments, others = attributes_names.partition { |name| attachments?(name) }
params = others.map { |name| ":#{name}" }
params += attachments.map { |name| "#{name}: []" }
params.join(", ")
end
- 1
def attachments?(name)
attribute = attributes.find { |attr| attr.name == name }
attribute&.attachments?
end
end
end
end
# frozen_string_literal: true
- 1
module Rails
- 1
module Generators
- 1
class SystemTestGenerator < NamedBase # :nodoc:
- 1
hook_for :system_tests, as: :system
end
end
end
# frozen_string_literal: true
- 1
module Rails
- 1
module Generators
- 1
class TaskGenerator < NamedBase # :nodoc:
- 1
argument :actions, type: :array, default: [], banner: "action action"
- 1
def create_task_files
template "task.rb", File.join("lib/tasks", "#{file_name}.rake")
end
end
end
end
# frozen_string_literal: true
- 2
require "rails/generators/active_model"
- 2
require "rails/generators/model_helpers"
- 2
module Rails
- 2
module Generators
# Deal with controller names on scaffold and add some helpers to deal with
# ActiveModel.
- 2
module ResourceHelpers # :nodoc:
- 2
def self.included(base) #:nodoc:
- 2
base.include(Rails::Generators::ModelHelpers)
- 2
base.class_option :model_name, type: :string, desc: "ModelName to be used"
end
# Set controller variables on initialization.
- 2
def initialize(*args) #:nodoc:
super
controller_name = name
if options[:model_name]
self.name = options[:model_name]
assign_names!(name)
end
assign_controller_names!(controller_name.pluralize)
end
- 2
private
- 2
attr_reader :controller_name, :controller_file_name
- 2
def controller_class_path
if options[:model_name]
@controller_class_path
else
class_path
end
end
- 2
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
- 2
def controller_file_path
@controller_file_path ||= (controller_class_path + [controller_file_name]).join("/")
end
- 2
def controller_class_name
(controller_class_path + [controller_file_name]).map!(&:camelize).join("::")
end
- 2
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 a specific method for the
# ORM. Check Rails::Generators::ActiveModel for more information.
- 2
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.
- 2
def orm_instance(name = singular_table_name)
@orm_instance ||= orm_class.new(name)
end
end
end
end
# frozen_string_literal: true
- 16
require "rails/generators"
- 16
require "rails/generators/testing/behaviour"
- 16
require "rails/generators/testing/setup_and_teardown"
- 16
require "rails/generators/testing/assertions"
- 16
require "fileutils"
- 16
module Rails
- 16
module Generators
# Disable color in output. Easier to debug.
- 16
no_color!
# This class provides a TestCase for testing generators. To set up, 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", __dir__)
# 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", __dir__)
# setup :prepare_destination
# end
- 16
class TestCase < ActiveSupport::TestCase
- 16
include Rails::Generators::Testing::Behaviour
- 16
include Rails::Generators::Testing::SetupAndTeardown
- 16
include Rails::Generators::Testing::Assertions
- 16
include FileUtils
end
end
end
# frozen_string_literal: true
require "rails/generators/named_base"
module TestUnit # :nodoc:
module Generators # :nodoc:
class Base < Rails::Generators::NamedBase # :nodoc:
end
end
end
# frozen_string_literal: true
require "rails/generators/test_unit"
module TestUnit # :nodoc:
module Generators # :nodoc:
class ControllerGenerator < Base # :nodoc:
argument :actions, type: :array, default: [], banner: "action action"
class_option :skip_routes, type: :boolean
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
# frozen_string_literal: true
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
private
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
# frozen_string_literal: true
require "rails/generators/test_unit"
module TestUnit # :nodoc:
module Generators # :nodoc:
class HelperGenerator < Base # :nodoc:
# Rails does not generate anything here.
end
end
end
# frozen_string_literal: true
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
private
def file_name
@_file_name ||= super.sub(/_test\z/i, "")
end
end
end
end
# frozen_string_literal: true
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", File.join("test/jobs", class_path, "#{file_name}_job_test.rb")
end
private
def file_name
@_file_name ||= super.sub(/_job\z/i, "")
end
end
end
end
# frozen_string_literal: true
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
private
def file_name
@_file_name ||= super.sub(/_mailer\z/i, "")
end
end
end
end
# frozen_string_literal: true
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
# frozen_string_literal: true
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
require "active_support/testing/autorun"
require "active_support"
# frozen_string_literal: true
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"
class_option :system_tests, type: :string,
desc: "Skip system test files"
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")
if !options.api? && options[:system_tests]
template "system_test.rb", File.join("test/system", class_path, "#{file_name.pluralize}_test.rb")
end
end
def fixture_name
@fixture_name ||=
if mountable_engine?
(namespace_dirs + [table_name]).join("_")
else
table_name
end
end
private
def attributes_string
attributes_hash.map { |k, v| "#{k}: #{v}" }.join(", ")
end
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'"]
elsif !virtual?(name)
["#{name}", "@#{singular_table_name}.#{name}"]
end
end.compact.sort.to_h
end
def boolean?(name)
attribute = attributes.find { |attr| attr.name == name }
attribute&.type == :boolean
end
def virtual?(name)
attribute = attributes.find { |attr| attr.name == name }
attribute&.virtual?
end
end
end
end
# frozen_string_literal: true
require "rails/generators/test_unit"
module TestUnit # :nodoc:
module Generators # :nodoc:
class SystemGenerator < Base # :nodoc:
check_class_collision suffix: "Test"
def create_test_files
if !File.exist?(File.join("test/application_system_test_case.rb"))
template "application_system_test_case.rb", File.join("test", "application_system_test_case.rb")
end
template "system_test.rb", File.join("test/system", class_path, "#{file_name.pluralize}_test.rb")
end
private
def file_name
@_file_name ||= super.sub(/_test\z/i, "")
end
end
end
end
# frozen_string_literal: true
- 16
module Rails
- 16
module Generators
- 16
module Testing
- 16
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
- 16
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?
assert_nothing_raised { 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
- 16
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"
- 16
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
- 16
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.
- 16
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"
- 16
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
- 16
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
- 16
def assert_instance_method(method, content)
assert content =~ /(\s+)def #{method}(\(.+\))?(.*?)\n\1end/m, "Expected to have method #{method}"
assert_nothing_raised { yield $3.strip } if block_given?
end
- 16
alias :assert_method :assert_instance_method
# Asserts the given attribute type gets translated to a field type
# properly:
#
# assert_field_type :date, :date_select
- 16
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"
- 16
def assert_field_default_value(attribute_type, value)
if value.nil?
assert_nil(create_generated_attribute(attribute_type).default)
else
assert_equal(value, create_generated_attribute(attribute_type).default)
end
end
end
end
end
end
# frozen_string_literal: true
- 16
require "active_support/core_ext/class/attribute"
- 16
require "active_support/core_ext/module/delegation"
- 16
require "active_support/core_ext/hash/reverse_merge"
- 16
require "active_support/core_ext/kernel/reporting"
- 16
require "active_support/testing/stream"
- 16
require "active_support/concern"
- 16
require "rails/generators"
- 16
module Rails
- 16
module Generators
- 16
module Testing
- 16
module Behaviour
- 16
extend ActiveSupport::Concern
- 16
include ActiveSupport::Testing::Stream
- 16
included do
# 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.
- 16
class_attribute :current_path, default: File.expand_path(Dir.pwd)
- 16
class_attribute :default_arguments, default: []
- 16
class_attribute :destination_root
- 16
class_attribute :generator_class
end
- 16
module ClassMethods
# Sets which generator should be tested:
#
# tests AppGenerator
- 16
def tests(klass)
- 22
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)
- 16
def arguments(array)
- 14
self.default_arguments = array
end
# Sets the destination of generator files:
#
# destination File.expand_path("../tmp", __dir__)
- 16
def destination(path)
- 19
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", __dir__)
# 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.
- 16
def run_generator(args = default_arguments, config = {})
capture(:stdout) do
args += ["--skip-bundle"] unless args.include? "--dev"
args |= ["--skip-bootsnap"] unless args.include? "--no-skip-bootsnap"
args |= ["--skip-webpack-install"] unless args.include? "--no-skip-webpack-install"
generator_class.start(args, config.reverse_merge(destination_root: destination_root))
end
end
# Instantiate the generator.
- 16
def generator(args = default_arguments, options = {}, config = {})
@generator ||= 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')
- 16
def create_generated_attribute(attribute_type, name = "test", index = nil)
Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(":"))
end
- 16
private
- 16
def destination_root_is_set?
raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
end
- 16
def ensure_current_path
cd current_path
end
# Clears all files and directories in destination.
- 16
def prepare_destination # :doc:
rm_rf(destination_root)
mkdir_p(destination_root)
end
- 16
def migration_file_name(relative)
absolute = File.expand_path(relative, destination_root)
dirname, file_name = File.dirname(absolute), File.basename(absolute).delete_suffix(".rb")
Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
end
end
end
end
end
# frozen_string_literal: true
- 16
module Rails
- 16
module Generators
- 16
module Testing
- 16
module SetupAndTeardown
- 16
def setup # :nodoc:
destination_root_is_set?
ensure_current_path
super
end
- 16
def teardown # :nodoc:
ensure_current_path
super
end
end
end
end
end
# frozen_string_literal: true
require "cgi"
module Rails
# This module helps build the runtime properties that are displayed in
# Rails::InfoController responses. These include the active Rails version,
# Ruby version, Rack version, and so on.
module Info
mattr_accessor :properties, default: []
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_DESCRIPTION
end
# The RubyGems version, if it's installed.
property "RubyGems version" do
Gem::VERSION
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.connection.pool.db_config.adapter
end
property "Database schema version" do
ActiveRecord::Base.connection.migration_context.current_version rescue nil
end
end
end
# frozen_string_literal: true
require "rails/application_controller"
require "action_dispatch/routing/inspector"
class Rails::InfoController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugView::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::DEFAULT_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
# frozen_string_literal: true
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
def context_class
@context.class
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
# frozen_string_literal: true
require "rails/application_controller"
class Rails::MailersController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
around_action :set_locale, only: :preview
before_action :find_preview, only: :preview
before_action :require_local!, unless: :show_previews?
helper_method :part_query, :locale_query
content_security_policy(false)
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)
@page_title = "Mailer Preview for #{@preview.preview_name}##{@email_action}"
@email = @preview.call(@email_action, params)
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: [:html]
end
else
raise AbstractController::ActionNotFound, "Email '#{@email_action}' not found in #{@preview.name}"
end
end
end
private
def show_previews? # :doc:
ActionMailer::Base.show_previews
end
def find_preview # :doc:
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) # :doc:
formats.each do |format|
if part = @email.find_first_mime_type(format)
return part
end
end
if formats.any? { |f| @email.mime_type == f }
end
end
def find_part(format) # :doc:
if part = @email.find_first_mime_type(format)
part
elsif @email.mime_type == format
end
end
def part_query(mime_type)
request.query_parameters.merge(part: mime_type).to_query
end
def locale_query(locale)
request.query_parameters.merge(locale: locale).to_query
end
def set_locale
I18n.with_locale(params[:locale] || I18n.default_locale) do
yield
end
end
end
# frozen_string_literal: true
require "pathname"
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 through 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 above command 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)
@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]
@exclude = options[:exclude]
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 paths
raise "You need to set a path root" unless @root.path
map do |p|
Pathname.new(@root.path).join(p)
end
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 |path|
path = File.expand_path(path, @root.path)
if @glob && File.directory?(path)
result.concat files_in(path)
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 do |f|
does_exist = File.exist?(f)
if !does_exist && File.symlink?(f)
raise "File #{f.inspect} is a symlink that does not point to a valid file"
end
does_exist
end
end
def existent_directories
expanded.select { |d| File.directory?(d) }
end
alias to_a expanded
private
def files_in(path)
files = Dir.glob(@glob, base: path)
files -= @exclude if @exclude
files.map! { |file| File.join(path, file) }
files.sort
end
end
end
end
# frozen_string_literal: true
require "rails/test_unit/runner"
require "rails/test_unit/reporter"
Rails::TestUnitReporter.executable = "bin/test"
Rails::TestUnit::Runner.parse_options(ARGV)
Rails::TestUnit::Runner.run(ARGV)
# frozen_string_literal: true
module Rails
module Rack
autoload :Logger, "rails/rack/logger"
end
end
# frozen_string_literal: true
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
private
def call_app(request, env) # :doc:
instrumenter = ActiveSupport::Notifications.instrumenter
instrumenter.start "request.action_dispatch", request: request
logger.info { started_request_message(request) }
status, headers, body = @app.call(env)
body = ::Rack::BodyProxy.new(body) { finish(request) }
[status, headers, body]
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) # :doc:
'Started %s "%s" for %s at %s' % [
request.request_method,
request.filtered_path,
request.remote_ip,
Time.now.to_default_s ]
end
def compute_tags(request) # :doc:
@taggers.collect do |tag|
case tag
when Proc
tag.call(request)
when Symbol
request.send(tag)
else
tag
end
end
end
def finish(request)
instrumenter = ActiveSupport::Notifications.instrumenter
instrumenter.finish "request.action_dispatch", request: request
end
def logger
Rails.logger
end
end
end
end
# frozen_string_literal: true
require "rails/initializable"
require "active_support/inflector"
require "active_support/core_ext/module/introspection"
require "active_support/core_ext/module/delegation"
module Rails
# <tt>Rails::Railtie</tt> 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, Active
# Record, etc.) implements 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 implementing a railtie, but
# if you need to interact with the Rails framework during or after boot, then
# a railtie is needed.
#
# For example, an extension doing any of the following would need a 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 <tt>ActiveSupport::Notifications</tt>
# * adding Rake tasks
#
# == Creating a Railtie
#
# To extend Rails using a railtie, create a subclass of <tt>Rails::Railtie</tt>.
# This class must be loaded during the Rails boot process, and is conventionally
# called <tt>MyNamespace::Railtie</tt>.
#
# 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::Railtie)
#
# == Initializers
#
# To add an initialization step to the Rails boot process from your railtie, just
# define the initialization code with the +initializer+ macro:
#
# 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 options to
# +initializer+, in case you want to couple it with a specific step in the
# initialization process.
#
# == Configuration
#
# Railties 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
#
# Since filenames on the load path are shared across gems, be sure that files you load
# through a railtie have unique names.
#
# == Application and Engine
#
# An engine is nothing more than a railtie with some initializers already set. And since
# <tt>Rails::Application</tt> 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)
register_block_for(:rake_tasks, &blk)
end
def console(&blk)
register_block_for(:load_console, &blk)
end
def runner(&blk)
register_block_for(:runner, &blk)
end
def generators(&blk)
register_block_for(:generators, &blk)
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
# 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
private
def generate_railtie_name(string)
ActiveSupport::Inflector.underscore(string).tr("/", "_")
end
def respond_to_missing?(name, _)
instance.respond_to?(name) || super
end
# If the class method does not have a method, then send the method call
# to the Railtie instance.
def method_missing(name, *args, &block)
if instance.respond_to?(name)
instance.public_send(name, *args, &block)
else
super
end
end
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
# receives an instance variable identifier, set the variable value if is
# blank and append given block to value, which will be used later in
# `#each_registered_block(type, &block)`
def register_block_for(type, &blk)
var_name = "@#{type}"
blocks = instance_variable_defined?(var_name) ? instance_variable_get(var_name) : instance_variable_set(var_name, [])
blocks << blk if blk
blocks
end
end
delegate :railtie_name, to: :class
def initialize #:nodoc:
if self.class.abstract_railtie?
raise "#{self.class.name} is abstract, you cannot instantiate it directly."
end
end
def configure(&block) #:nodoc:
instance_eval(&block)
end
# This is used to create the <tt>config</tt> object on Railties, an instance of
# Railtie::Configuration, that is used by Railties and Application to store
# related configuration.
def config
@config ||= Railtie::Configuration.new
end
def railtie_namespace #:nodoc:
@railtie_namespace ||= self.class.module_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
# run `&block` in every registered block in `#register_block_for`
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
# frozen_string_literal: true
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 #{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
private
def method_missing(*args, &block)
instance.send(*args, &block)
end
end
end
end
end
# frozen_string_literal: true
require "rails/configuration"
require "active_support/core_ext/symbol/starts_ends_with"
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.eager_load+
# 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.end_with?("=")
@@options[:"#{name[0..-2]}"] = args.first
elsif @@options.key?(name)
@@options[name]
else
super
end
end
end
end
end
# frozen_string_literal: true
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5.0") && RUBY_ENGINE == "ruby"
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
Rails 6 requires Ruby 2.5.0 or newer.
You're running
#{desc}
Please upgrade to Ruby 2.5.0 or newer to continue.
end_message
end
# frozen_string_literal: true
require "yaml"
require "active_support/message_encryptor"
module Rails
# Greatly inspired by Ara T. Howard's magnificent sekrets gem. 😘
class Secrets # :nodoc:
class MissingKeyError < RuntimeError
def initialize
super(<<-end_of_message.squish)
Missing encryption key to decrypt secrets with.
Ask your team for your master key and put it in ENV["RAILS_MASTER_KEY"]
end_of_message
end
end
@cipher = "aes-128-gcm"
@root = File # Wonky, but ensures `join` uses the current directory.
class << self
attr_writer :root
def parse(paths, env:)
paths.each_with_object(Hash.new) do |path, all_secrets|
require "erb"
secrets = YAML.load(ERB.new(preprocess(path)).result) || {}
all_secrets.merge!(secrets["shared"].deep_symbolize_keys) if secrets["shared"]
all_secrets.merge!(secrets[env].deep_symbolize_keys) if secrets[env]
end
end
def key
ENV["RAILS_MASTER_KEY"] || read_key_file || handle_missing_key
end
def encrypt(data)
encryptor.encrypt_and_sign(data)
end
def decrypt(data)
encryptor.decrypt_and_verify(data)
end
def read
decrypt(IO.binread(path))
end
def write(contents)
IO.binwrite("#{path}.tmp", encrypt(contents))
FileUtils.mv("#{path}.tmp", path)
end
def read_for_editing(&block)
writing(read, &block)
end
private
def handle_missing_key
raise MissingKeyError
end
def read_key_file
if File.exist?(key_path)
IO.binread(key_path).strip
end
end
def key_path
@root.join("config", "secrets.yml.key")
end
def path
@root.join("config", "secrets.yml.enc").to_s
end
def preprocess(path)
if path.end_with?(".enc")
decrypt(IO.binread(path))
else
IO.read(path)
end
end
def writing(contents)
tmp_file = "#{File.basename(path)}.#{Process.pid}"
tmp_path = File.join(Dir.tmpdir, tmp_file)
IO.binwrite(tmp_path, contents)
yield tmp_path
updated_contents = IO.binread(tmp_path)
write(updated_contents) if updated_contents != contents
ensure
FileUtils.rm(tmp_path) if File.exist?(tmp_path)
end
def encryptor
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: @cipher)
end
end
end
end
# frozen_string_literal: true
- 17
require "active_support/deprecation"
- 17
module Rails
# Implements the logic behind <tt>Rails::Command::NotesCommand</tt>. See <tt>rails notes --help</tt> for usage information.
#
# 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.
- 17
class SourceAnnotationExtractor
- 17
class Annotation < Struct.new(:line, :tag, :text)
- 17
def self.directories
@@directories ||= %w(app config db lib test)
end
# Registers additional directories to be included
# Rails::SourceAnnotationExtractor::Annotation.register_directories("spec", "another")
- 17
def self.register_directories(*dirs)
directories.push(*dirs)
end
- 17
def self.tags
@@tags ||= %w(OPTIMIZE FIXME TODO)
end
# Registers additional tags
# Rails::SourceAnnotationExtractor::Annotation.register_tags("TESTME", "DEPRECATEME")
- 17
def self.register_tags(*additional_tags)
tags.push(*additional_tags)
end
- 17
def self.extensions
- 51
@@extensions ||= {}
end
# Registers new Annotations File Extensions
# Rails::SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
- 17
def self.register_extensions(*exts, &block)
- 51
extensions[/\.(#{exts.join("|")})$/] = block
end
- 17
register_extensions("builder", "rb", "rake", "yml", "yaml", "ruby") { |tag| /#\s*(#{tag}):?\s*(.*)$/ }
- 17
register_extensions("css", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
- 17
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.
- 17
def to_s(options = {})
s = +"[#{line.to_s.rjust(options[:indent])}] "
s << "[#{tag}] " if options[:tag]
s << text
end
# Used in annotations.rake
#:nodoc:
- 17
def self.notes_task_deprecation_warning
ActiveSupport::Deprecation.warn("This rake task is deprecated and will be removed in Rails 6.1. \nRefer to `rails notes --help` for more information.\n")
puts "\n"
end
end
# Prints all annotations with tag +tag+ under the root directories +app+,
# +config+, +db+, +lib+, and +test+ (recursively).
#
# If +tag+ is <tt>nil</tt>, annotations with either default or registered tags are printed.
#
# Specific directories can be explicitly set using the <tt>:dirs</tt> key in +options+.
#
# Rails::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 `rails notes` command.
- 17
def self.enumerate(tag = nil, options = {})
tag ||= Annotation.tags.join("|")
extractor = new(tag)
dirs = options.delete(:dirs) || Annotation.directories
extractor.display(extractor.find(dirs), options)
end
- 17
attr_reader :tag
- 17
def initialize(tag)
@tag = tag
end
# Returns a hash that maps filenames under +dirs+ (recursively) to arrays
# with their annotations.
- 17
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. Files with extensions registered in
# <tt>Rails::SourceAnnotationExtractor::Annotation.extensions</tt> are
# taken into account. Only files with annotations are included.
- 17
def find_in(dir)
results = {}
Dir.glob("#{dir}/*") do |item|
next if File.basename(item).start_with?(".")
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.
- 17
def extract_annotations_from(file, pattern)
lineno = 0
result = File.readlines(file, encoding: Encoding::BINARY).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+.
- 17
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
end
# Remove this deprecated class in the next minor version
#:nodoc:
- 17
SourceAnnotationExtractor = ActiveSupport::Deprecation::DeprecatedConstantProxy.
new("SourceAnnotationExtractor", "Rails::SourceAnnotationExtractor")
# frozen_string_literal: true
require "rake"
# Load Rails Rakefile extensions
%w(
annotations
dev
framework
initializers
log
middleware
misc
restart
routes
tmp
yarn
zeitwerk
).tap { |arr|
arr << "statistics" if Rake.application.current_scope.empty?
}.each do |task|
load "rails/tasks/#{task}.rake"
end
# frozen_string_literal: true
# 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 "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)
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
ActiveSupport.on_load(:active_support_test_case) do
include ActiveRecord::TestDatabases
include ActiveRecord::TestFixtures
self.fixture_path = "#{Rails.root}/test/fixtures/"
self.file_fixture_path = fixture_path + "files"
end
ActiveSupport.on_load(:action_dispatch_integration_test) do
self.fixture_path = ActiveSupport::TestCase.fixture_path
end
end
# :enddoc:
ActiveSupport.on_load(:action_controller_test_case) do
def before_setup # :nodoc:
@routes = Rails.application.routes
super
end
end
ActiveSupport.on_load(:action_dispatch_integration_test) do
def before_setup # :nodoc:
@routes = Rails.application.routes
super
end
end
# frozen_string_literal: true
- 17
require "rails/test_unit/runner"
- 17
module Rails
- 17
module LineFiltering # :nodoc:
- 17
def run(reporter, options = {})
options[:filter] = Rails::TestUnit::Runner.compose_filter(self, options[:filter])
super
end
end
end
# frozen_string_literal: true
- 17
require "rails/test_unit/line_filtering"
- 17
if defined?(Rake.application) && Rake.application.top_level_tasks.grep(/^(default$|test(:|$))/).any?
ENV["RAILS_ENV"] ||= Rake.application.options.show_tasks ? "development" : "test"
end
- 17
module Rails
- 17
class TestUnitRailtie < Rails::Railtie
- 17
config.app_generators do |c|
- 17
c.test_framework :test_unit, fixture: true,
fixture_replacement: nil
- 17
c.integration_tool :test_unit
- 17
c.system_tests :test_unit
end
- 17
initializer "test_unit.line_filtering" do
ActiveSupport.on_load(:active_support_test_case) {
ActiveSupport::TestCase.extend Rails::LineFiltering
}
end
- 17
rake_tasks do
load "rails/test_unit/testing.rake"
end
end
end
# frozen_string_literal: true
require "active_support/core_ext/class/attribute"
require "minitest"
module Rails
class TestUnitReporter < Minitest::StatisticsReporter
class_attribute :app_root
class_attribute :executable, default: "rails test"
def record(result)
super
if options[:verbose]
io.puts color_output(format_line(result), by: result)
else
io.print color_output(result.result_code, by: result)
end
if output_inline? && result.failure && (!result.skipped? || options[:verbose])
io.puts
io.puts
io.puts color_output(result, by: result)
io.puts
io.puts format_rerun_snippet(result)
io.puts
end
if fail_fast? && result.failure && !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_line(result)
klass = result.respond_to?(:klass) ? result.klass : result.class
"%s#%s = %.2f s = %s" % [klass, result.name, result.time, result.result_code]
end
def format_rerun_snippet(result)
location, line = if result.respond_to?(:source_location)
result.source_location
else
result.method(result.name).source_location
end
"#{executable} #{relative_path_for(location)}:#{line}"
end
def app_root
@app_root ||= self.class.app_root ||
if defined?(ENGINE_ROOT)
ENGINE_ROOT
elsif Rails.respond_to?(:root)
Rails.root
end
end
def colored_output?
options[:color] && io.respond_to?(:tty?) && io.tty?
end
codes = { red: 31, green: 32, yellow: 33 }
COLOR_BY_RESULT_CODE = {
"." => codes[:green],
"E" => codes[:red],
"F" => codes[:red],
"S" => codes[:yellow]
}
def color_output(string, by:)
if colored_output?
"\e[#{COLOR_BY_RESULT_CODE[by.result_code]}m#{string}\e[0m"
else
string
end
end
end
end
# frozen_string_literal: true
- 17
require "shellwords"
- 17
require "method_source"
- 17
require "rake/file_list"
- 17
require "active_support/core_ext/module/attribute_accessors"
- 17
module Rails
- 17
module TestUnit
- 17
class Runner
- 17
mattr_reader :filters, default: []
- 17
class << self
- 17
def attach_before_load_options(opts)
opts.on("--warnings", "-w", "Run with Ruby warnings enabled") { }
opts.on("-e", "--environment ENV", "Run tests in the ENV environment") { }
end
- 17
def parse_options(argv)
# Perform manual parsing and cleanup since option parser raises on unknown options.
env_index = argv.index("--environment") || argv.index("-e")
if env_index
argv.delete_at(env_index)
environment = argv.delete_at(env_index).strip
end
ENV["RAILS_ENV"] = environment || "test"
w_index = argv.index("--warnings") || argv.index("-w")
$VERBOSE = argv.delete_at(w_index) if w_index
end
- 17
def rake_run(argv = [])
ARGV.replace Shellwords.split(ENV["TESTOPTS"] || "")
run(argv)
end
- 17
def run(argv = [])
load_tests(argv)
require "active_support/testing/autorun"
end
- 17
def load_tests(argv)
patterns = extract_filters(argv)
tests = Rake::FileList[patterns.any? ? patterns : default_test_glob]
tests.exclude(default_test_exclude_glob) if patterns.empty?
tests.to_a.each { |path| require File.expand_path(path) }
end
- 17
def compose_filter(runnable, filter)
if filters.any? { |_, lines| lines.any? }
CompositeFilter.new(runnable, filter, filters)
else
filter
end
end
- 17
private
- 17
def extract_filters(argv)
# Extract absolute and relative paths but skip -n /.*/ regexp filters.
argv.select { |arg| path_argument?(arg) && !regexp_filter?(arg) }.map do |path|
path = path.tr("\\", "/")
case
when /(:\d+)+$/.match?(path)
file, *lines = path.split(":")
filters << [ file, lines ]
file
when Dir.exist?(path)
"#{path}/**/*_test.rb"
else
filters << [ path, [] ]
path
end
end
end
- 17
def default_test_glob
ENV["DEFAULT_TEST"] || "test/**/*_test.rb"
end
- 17
def default_test_exclude_glob
ENV["DEFAULT_TEST_EXCLUDE"] || "test/{system,dummy}/**/*_test.rb"
end
- 17
def regexp_filter?(arg)
arg.start_with?("/") && arg.end_with?("/")
end
- 17
def path_argument?(arg)
%r"^[/\\]?\w+[/\\]".match?(arg)
end
end
end
- 17
class CompositeFilter # :nodoc:
- 17
attr_reader :named_filter
- 17
def initialize(runnable, filter, patterns)
@runnable = runnable
@named_filter = derive_named_filter(filter)
@filters = [ @named_filter, *derive_line_filters(patterns) ].compact
end
# minitest uses === to find matching filters.
- 17
def ===(method)
@filters.any? { |filter| filter === method }
end
- 17
private
- 17
def derive_named_filter(filter)
if filter.respond_to?(:named_filter)
filter.named_filter
elsif filter =~ %r%/(.*)/% # Regexp filtering copied from minitest.
Regexp.new $1
elsif filter.is_a?(String)
filter
end
end
- 17
def derive_line_filters(patterns)
patterns.flat_map do |file, lines|
if lines.empty?
Filter.new(@runnable, file, nil) if file
else
lines.map { |line| Filter.new(@runnable, file, line) }
end
end
end
end
- 17
class Filter # :nodoc:
- 17
def initialize(runnable, file, line)
@runnable, @file = runnable, File.expand_path(file)
@line = line.to_i if line
end
- 17
def ===(method)
return unless @runnable.method_defined?(method)
if @line
test_file, test_range = definition_for(@runnable.instance_method(method))
test_file == @file && test_range.include?(@line)
else
@runnable.instance_method(method).source_location.first == @file
end
end
- 17
private
- 17
def definition_for(method)
file, start_line = method.source_location
end_line = method.source.count("\n") + start_line - 1
return file, start_line..end_line
end
end
end
end
# frozen_string_literal: true
require_relative "gem_version"
module Rails
# Returns the version of the currently loaded Rails as a string.
def self.version
VERSION::STRING
end
end
# frozen_string_literal: true
require "rails/application_controller"
class Rails::WelcomeController < Rails::ApplicationController # :nodoc:
layout false
def index
end
end