CodeVetter · Repo Unpack · Behavior traces
rails/rails
Four flows are fully ordered in source: process start for an existing app, one HTTP request after boot, an Active Record insert, and editing encrypted credentials. Action Cable's socket open and Active Job's perform_later are shorter traces on the same boot. Names below are the methods actually called, not a conceptual MVC sketch.
- Startup outside an app: railties/exe/rails requires rails/cli; cli.rb calls Rails::AppLoader.exec_app; if that returns, ARGV is help, plugin, or application, and Rails::Command.invoke dispatches gem_help, plugin, or application (rails new).
- Startup inside an app: AppLoader#exec_app finds bin/rails and execs it, so the gem CLI stops. The binstub eventually loads railties/lib/rails/commands.rb, which shifts ARGV and calls Rails::Command.invoke. Command#invoke splits namespace, find_by_namespace loads *_command.rb, and command.perform runs, falling through to the rake command when no Thor command matches.
- rails server order: ServerCommand#perform calls set_application_directory! and prepare_restart, constructs Rails::Server with server_options, requires APP_PATH, Dir.chdir(Rails.application.root), then Server#start. start traps INT, create_tmp_directories (tmp/cache, tmp/pids, tmp/sockets), setup_dev_caching in development, optionally log_to_stdout, then Rackup::Server#start.
- Application boot order inside require APP_PATH: the Application subclass is defined, then initialize!(group = :default) calls run_initializers. initializers concatenates Bootstrap (:load_environment_hook, :load_active_support, :set_eager_load, :initialize_logger, :initialize_error_reporter, :initialize_event_reporter, :initialize_cache, :setup_once_autoloader, :bootstrap_hook), each railtie's initializers, then Finisher (:setup_default_session_store, :build_middleware_stack, :eager_load!, :add_internal_routes, :set_routes_reloader_hook, :enable_yjit).
- Request order after the middleware stack: RouteSet#call -> make_request -> Journey::Router#serve -> recognize -> route.app.serve. For a dispatcher endpoint that is Dispatcher#serve -> controller(req) -> make_response! -> dispatch -> ActionController::Metal#dispatch -> set_request! -> set_response! -> AbstractController::Base#process -> Callbacks#process_action -> the action method.
- Forgery checks are before-actions installed by protect_from_forgery: verify_authenticity_token and verify_request_for_forgery_protection, plus after-actions verify_same_origin_request and append_sec_fetch_site_to_vary_header. verified_request? allows GET, HEAD, a real QUERY (not a POST tunneled via _method=query), or a forgery strategy pass. load_defaults 8.2 sets the strategy to :header_only and the failure strategy to :exception.
- Insert order: Model#save -> create_or_update -> _create_record -> attributes_for_create -> self.class.with_connection -> _returning_columns_for_insert(connection) -> _insert_record -> type-cast returned columns -> @new_record = false. Update order is _update_record -> attributes_for_update -> _update_row, and an empty attribute list skips the SQL and sets @_trigger_update_callback. delete skips callbacks; destroy runs them.
- Connection acquisition order for that insert: establish_connection (usually from the railtie reading database.yml) -> connection_handler.establish_connection -> later with_connection -> ConnectionPool#lease_connection -> checkout on first use for the thread. ActiveRecord.disconnect_all! closes every pool via PoolConfig.disconnect_all!.
- credentials:edit order: load_environment_config!, load_generators, maybe retarget content_path and key_path to config/credentials/<env>.yml.enc, ensure_encryption_key_has_been_added (EncryptionKeyFileGenerator#add_key_file), ensure_credentials_have_been_added (CredentialsGenerator#invoke_all), then credentials.change yields a tempfile to system_editor, which is system(*Shellwords.split(editor), path). InvalidMessage and MissingKeyError are rescued and printed.
- Action Cable open order on the server connection object: handle_open calls connect (empty by default; apps override it to identify the current user), then subscribe_to_internal_channel, then send_welcome_message. Authorization::UnauthorizedError closes with disconnect reason unauthorized and reconnect: false. Incoming frames go handle_channel_command -> subscriptions.execute_command. Close is unsubscribe_from_all, unsubscribe_from_internal_channel, disconnect.
- Job order: SomeJob.perform_later -> job_or_instantiate -> job.enqueue. QueueAdapter::ClassMethods#queue_adapter lazily assigns :async, and queue_adapter= Symbol/String calls ActiveJob::QueueAdapters.lookup(name).new then check_adapter if the adapter defines it. A custom object is accepted only when it responds to enqueue and enqueue_at.