rubino-agent 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +115 -0
- data/.rubocop_todo.yml +955 -0
- data/.ruby-version +1 -0
- data/AGENTS.md +97 -0
- data/CHANGELOG.md +344 -0
- data/CONTRIBUTING.md +69 -0
- data/LICENSE +21 -0
- data/README.md +200 -0
- data/Rakefile +8 -0
- data/docs/agents.md +190 -0
- data/docs/api/v1.md +414 -0
- data/docs/architecture.md +177 -0
- data/docs/commands.md +375 -0
- data/docs/configuration.md +590 -0
- data/docs/getting-started.md +143 -0
- data/docs/jobs.md +332 -0
- data/docs/mcp.md +128 -0
- data/docs/memory.md +98 -0
- data/docs/models-and-keys.md +173 -0
- data/docs/oauth-providers.md +145 -0
- data/docs/plugins.md +195 -0
- data/docs/security.md +145 -0
- data/docs/skills.md +322 -0
- data/docs/tools.md +395 -0
- data/docs/troubleshooting.md +73 -0
- data/exe/rubino +9 -0
- data/install.sh +275 -0
- data/lib/rubino/active_skill.rb +50 -0
- data/lib/rubino/agent/agent_registry.rb +120 -0
- data/lib/rubino/agent/backoff_policy.rb +116 -0
- data/lib/rubino/agent/definition.rb +128 -0
- data/lib/rubino/agent/degenerate_recovery.rb +271 -0
- data/lib/rubino/agent/fallback_chain.rb +194 -0
- data/lib/rubino/agent/iteration_budget.rb +50 -0
- data/lib/rubino/agent/loop.rb +617 -0
- data/lib/rubino/agent/model_call_runner.rb +383 -0
- data/lib/rubino/agent/prompts/build.txt +69 -0
- data/lib/rubino/agent/prompts/compaction.txt +20 -0
- data/lib/rubino/agent/prompts/explore.txt +19 -0
- data/lib/rubino/agent/prompts/general.txt +20 -0
- data/lib/rubino/agent/prompts/plan.txt +31 -0
- data/lib/rubino/agent/response_validator.rb +70 -0
- data/lib/rubino/agent/router.rb +65 -0
- data/lib/rubino/agent/runner.rb +195 -0
- data/lib/rubino/agent/tool_executor.rb +402 -0
- data/lib/rubino/agent/truncation_continuation.rb +137 -0
- data/lib/rubino/api/middleware/auth.rb +43 -0
- data/lib/rubino/api/middleware/error_handler.rb +65 -0
- data/lib/rubino/api/middleware/json_parser.rb +100 -0
- data/lib/rubino/api/middleware/observability.rb +59 -0
- data/lib/rubino/api/middleware/rate_limit.rb +136 -0
- data/lib/rubino/api/operations/approvals/decide_operation.rb +49 -0
- data/lib/rubino/api/operations/clarifications/decide_operation.rb +44 -0
- data/lib/rubino/api/operations/cron_jobs/create_operation.rb +46 -0
- data/lib/rubino/api/operations/cron_jobs/delete_operation.rb +36 -0
- data/lib/rubino/api/operations/cron_jobs/list_operation.rb +55 -0
- data/lib/rubino/api/operations/cron_jobs/pause_operation.rb +34 -0
- data/lib/rubino/api/operations/cron_jobs/resume_operation.rb +34 -0
- data/lib/rubino/api/operations/cron_jobs/schedule_validation.rb +30 -0
- data/lib/rubino/api/operations/cron_jobs/show_operation.rb +32 -0
- data/lib/rubino/api/operations/cron_jobs/trigger_operation.rb +38 -0
- data/lib/rubino/api/operations/cron_jobs/update_operation.rb +42 -0
- data/lib/rubino/api/operations/files/read_operation.rb +40 -0
- data/lib/rubino/api/operations/files/upload_operation.rb +175 -0
- data/lib/rubino/api/operations/health_operation.rb +46 -0
- data/lib/rubino/api/operations/memory/delete_operation.rb +32 -0
- data/lib/rubino/api/operations/memory/index_operation.rb +80 -0
- data/lib/rubino/api/operations/memory/stats_operation.rb +28 -0
- data/lib/rubino/api/operations/metrics_operation.rb +18 -0
- data/lib/rubino/api/operations/mode/show_operation.rb +29 -0
- data/lib/rubino/api/operations/mode/update_operation.rb +42 -0
- data/lib/rubino/api/operations/models/list_operation.rb +45 -0
- data/lib/rubino/api/operations/oauth/connections/disconnect_operation.rb +77 -0
- data/lib/rubino/api/operations/oauth/connections/list_operation.rb +36 -0
- data/lib/rubino/api/operations/oauth/providers/callback_operation.rb +82 -0
- data/lib/rubino/api/operations/oauth/providers/connect_operation.rb +44 -0
- data/lib/rubino/api/operations/oauth/providers/list_operation.rb +35 -0
- data/lib/rubino/api/operations/oauth/serializer.rb +21 -0
- data/lib/rubino/api/operations/runs/create_operation.rb +77 -0
- data/lib/rubino/api/operations/runs/events_operation.rb +195 -0
- data/lib/rubino/api/operations/runs/stop_operation.rb +34 -0
- data/lib/rubino/api/operations/sessions/create_operation.rb +46 -0
- data/lib/rubino/api/operations/sessions/delete_operation.rb +33 -0
- data/lib/rubino/api/operations/sessions/index_operation.rb +82 -0
- data/lib/rubino/api/operations/sessions/retry_operation.rb +45 -0
- data/lib/rubino/api/operations/sessions/show_operation.rb +59 -0
- data/lib/rubino/api/operations/sessions/undo_operation.rb +38 -0
- data/lib/rubino/api/operations/skills/list_operation.rb +34 -0
- data/lib/rubino/api/operations/skills/toggle_operation.rb +40 -0
- data/lib/rubino/api/operations/tasks/index_operation.rb +30 -0
- data/lib/rubino/api/operations/tasks/serializer.rb +60 -0
- data/lib/rubino/api/operations/tasks/show_operation.rb +33 -0
- data/lib/rubino/api/operations/tasks/stop_operation.rb +47 -0
- data/lib/rubino/api/request.rb +54 -0
- data/lib/rubino/api/responses.rb +64 -0
- data/lib/rubino/api/router.rb +72 -0
- data/lib/rubino/api/schemas.rb +103 -0
- data/lib/rubino/api/server.rb +102 -0
- data/lib/rubino/api/tls.rb +108 -0
- data/lib/rubino/attachments/classification.rb +16 -0
- data/lib/rubino/attachments/classify.rb +171 -0
- data/lib/rubino/attachments/defang.rb +47 -0
- data/lib/rubino/attachments/policy.rb +36 -0
- data/lib/rubino/attachments/preamble.rb +120 -0
- data/lib/rubino/boot/encryption_key.rb +32 -0
- data/lib/rubino/cli/chat/bang_shell.rb +257 -0
- data/lib/rubino/cli/chat/completion_builder.rb +290 -0
- data/lib/rubino/cli/chat/idle_card_host.rb +69 -0
- data/lib/rubino/cli/chat/image_inbox.rb +168 -0
- data/lib/rubino/cli/chat/session_resolver.rb +176 -0
- data/lib/rubino/cli/chat_command.rb +1674 -0
- data/lib/rubino/cli/commands.rb +250 -0
- data/lib/rubino/cli/config_command.rb +96 -0
- data/lib/rubino/cli/doctor_command.rb +251 -0
- data/lib/rubino/cli/jobs_command.rb +60 -0
- data/lib/rubino/cli/memory_command.rb +135 -0
- data/lib/rubino/cli/onboarding_wizard.rb +207 -0
- data/lib/rubino/cli/server_command.rb +139 -0
- data/lib/rubino/cli/session_command.rb +125 -0
- data/lib/rubino/cli/setup_command.rb +107 -0
- data/lib/rubino/cli/skills_command.rb +85 -0
- data/lib/rubino/cli/tools_command.rb +81 -0
- data/lib/rubino/cli/trust_gate.rb +71 -0
- data/lib/rubino/commands/built_ins.rb +46 -0
- data/lib/rubino/commands/command.rb +116 -0
- data/lib/rubino/commands/executor.rb +550 -0
- data/lib/rubino/commands/handlers/agents.rb +510 -0
- data/lib/rubino/commands/handlers/config.rb +88 -0
- data/lib/rubino/commands/handlers/help.rb +148 -0
- data/lib/rubino/commands/handlers/jobs.rb +71 -0
- data/lib/rubino/commands/handlers/mcp.rb +229 -0
- data/lib/rubino/commands/handlers/memory.rb +200 -0
- data/lib/rubino/commands/handlers/sessions.rb +207 -0
- data/lib/rubino/commands/handlers/skills.rb +195 -0
- data/lib/rubino/commands/handlers/status.rb +211 -0
- data/lib/rubino/commands/loader.rb +90 -0
- data/lib/rubino/config/configuration.rb +455 -0
- data/lib/rubino/config/defaults.rb +569 -0
- data/lib/rubino/config/loader.rb +115 -0
- data/lib/rubino/config/reasoning_prefs.rb +67 -0
- data/lib/rubino/config/writer.rb +72 -0
- data/lib/rubino/context/compressor.rb +149 -0
- data/lib/rubino/context/environment_inspector.rb +176 -0
- data/lib/rubino/context/file_discovery.rb +45 -0
- data/lib/rubino/context/message_boundary.rb +39 -0
- data/lib/rubino/context/prompt_assembler.rb +382 -0
- data/lib/rubino/context/summary_builder.rb +159 -0
- data/lib/rubino/context/token_budget.rb +68 -0
- data/lib/rubino/context/tool_pair_sanitizer.rb +70 -0
- data/lib/rubino/database/connection.rb +77 -0
- data/lib/rubino/database/migrations/001_create_initial_schema.rb +156 -0
- data/lib/rubino/database/migrations/002_create_runs.rb +45 -0
- data/lib/rubino/database/migrations/003_create_skill_states.rb +15 -0
- data/lib/rubino/database/migrations/004_create_cron_jobs.rb +36 -0
- data/lib/rubino/database/migrations/005_create_oauth_connections.rb +27 -0
- data/lib/rubino/database/migrations/006_create_webhook_deliveries.rb +34 -0
- data/lib/rubino/database/migrations/007_create_messages_fts.rb +59 -0
- data/lib/rubino/database/migrations/008_create_memory_facts.rb +75 -0
- data/lib/rubino/database/migrations/009_create_memory_graph.rb +55 -0
- data/lib/rubino/database/migrations/010_add_owner_pid_to_sessions.rb +20 -0
- data/lib/rubino/database/migrator.rb +48 -0
- data/lib/rubino/documents/converters/csv.rb +79 -0
- data/lib/rubino/documents/converters/docx.rb +129 -0
- data/lib/rubino/documents/converters/html.rb +28 -0
- data/lib/rubino/documents/converters/json.rb +35 -0
- data/lib/rubino/documents/converters/pdf.rb +59 -0
- data/lib/rubino/documents/converters/plain.rb +68 -0
- data/lib/rubino/documents/converters/pptx.rb +64 -0
- data/lib/rubino/documents/converters/xlsx.rb +62 -0
- data/lib/rubino/documents/converters/xml.rb +45 -0
- data/lib/rubino/documents/html.rb +71 -0
- data/lib/rubino/documents/registry.rb +68 -0
- data/lib/rubino/documents/table.rb +63 -0
- data/lib/rubino/documents.rb +50 -0
- data/lib/rubino/errors.rb +119 -0
- data/lib/rubino/files/workspace.rb +93 -0
- data/lib/rubino/interaction/cancel_token.rb +43 -0
- data/lib/rubino/interaction/clipboard_image.rb +84 -0
- data/lib/rubino/interaction/event_bus.rb +48 -0
- data/lib/rubino/interaction/events.rb +101 -0
- data/lib/rubino/interaction/image_input.rb +127 -0
- data/lib/rubino/interaction/input_queue.rb +117 -0
- data/lib/rubino/interaction/lifecycle.rb +299 -0
- data/lib/rubino/interaction/probe.rb +65 -0
- data/lib/rubino/interaction/state.rb +56 -0
- data/lib/rubino/jobs/cron_job_repository.rb +75 -0
- data/lib/rubino/jobs/handlers/cleanup_sessions_job.rb +32 -0
- data/lib/rubino/jobs/handlers/compact_session_job.rb +21 -0
- data/lib/rubino/jobs/handlers/distill_skill_job.rb +186 -0
- data/lib/rubino/jobs/handlers/extract_memory_job.rb +37 -0
- data/lib/rubino/jobs/handlers/summarize_session_job.rb +21 -0
- data/lib/rubino/jobs/queue.rb +184 -0
- data/lib/rubino/jobs/registry.rb +45 -0
- data/lib/rubino/jobs/runner.rb +79 -0
- data/lib/rubino/jobs/scheduler.rb +138 -0
- data/lib/rubino/jobs/webhook_delivery.rb +225 -0
- data/lib/rubino/jobs/worker.rb +59 -0
- data/lib/rubino/llm/adapter_factory.rb +47 -0
- data/lib/rubino/llm/adapter_response.rb +65 -0
- data/lib/rubino/llm/auxiliary_client.rb +61 -0
- data/lib/rubino/llm/bedrock_bearer_client.rb +235 -0
- data/lib/rubino/llm/content_builder.rb +55 -0
- data/lib/rubino/llm/credential_check.rb +93 -0
- data/lib/rubino/llm/error_classifier.rb +364 -0
- data/lib/rubino/llm/fake_provider.rb +292 -0
- data/lib/rubino/llm/inline_think_filter.rb +58 -0
- data/lib/rubino/llm/model_catalog.rb +29 -0
- data/lib/rubino/llm/provider_resolver.rb +48 -0
- data/lib/rubino/llm/reasoning_manager.rb +100 -0
- data/lib/rubino/llm/request.rb +56 -0
- data/lib/rubino/llm/ruby_llm_adapter.rb +794 -0
- data/lib/rubino/llm/scenario_loader.rb +68 -0
- data/lib/rubino/llm/scenario_selector.rb +80 -0
- data/lib/rubino/llm/scenarios/agent-creates-cron-failure.yml +29 -0
- data/lib/rubino/llm/scenarios/agent-creates-cron.yml +36 -0
- data/lib/rubino/llm/scenarios/analysis.yml +501 -0
- data/lib/rubino/llm/scenarios/complex-analysis.yml +598 -0
- data/lib/rubino/llm/scenarios/failure.yml +65 -0
- data/lib/rubino/llm/scenarios/happy-path.yml +24 -0
- data/lib/rubino/llm/scenarios/provider-quota-completed.yml +14 -0
- data/lib/rubino/llm/scenarios/wide-table.yml +121 -0
- data/lib/rubino/llm/scenarios/with-approvals.yml +50 -0
- data/lib/rubino/llm/scenarios/with-artifacts.yml +98 -0
- data/lib/rubino/llm/scenarios/with-clarify.yml +32 -0
- data/lib/rubino/llm/scenarios/with-reasoning.yml +175 -0
- data/lib/rubino/llm/scenarios/with-uploads.yml +104 -0
- data/lib/rubino/llm/thinking_support.rb +84 -0
- data/lib/rubino/llm/tool_bridge.rb +89 -0
- data/lib/rubino/logger.rb +99 -0
- data/lib/rubino/mcp/manager.rb +180 -0
- data/lib/rubino/mcp/mcp_tool_wrapper.rb +69 -0
- data/lib/rubino/mcp.rb +57 -0
- data/lib/rubino/memory/backend.rb +104 -0
- data/lib/rubino/memory/backends/default.rb +101 -0
- data/lib/rubino/memory/backends/sqlite.rb +653 -0
- data/lib/rubino/memory/backends.rb +53 -0
- data/lib/rubino/memory/deduplicator.rb +74 -0
- data/lib/rubino/memory/extractor.rb +85 -0
- data/lib/rubino/memory/flusher.rb +31 -0
- data/lib/rubino/memory/retriever.rb +50 -0
- data/lib/rubino/memory/sqlite_extraction_prompt.rb +70 -0
- data/lib/rubino/memory/sqlite_graph.rb +154 -0
- data/lib/rubino/memory/store.rb +228 -0
- data/lib/rubino/memory/threat_scanner.rb +68 -0
- data/lib/rubino/metrics.rb +175 -0
- data/lib/rubino/modes.rb +93 -0
- data/lib/rubino/oauth/connection_repository.rb +95 -0
- data/lib/rubino/oauth/provider/github.rb +75 -0
- data/lib/rubino/oauth/provider/google.rb +59 -0
- data/lib/rubino/oauth/provider.rb +149 -0
- data/lib/rubino/oauth/registry.rb +86 -0
- data/lib/rubino/oauth/token_encryptor.rb +87 -0
- data/lib/rubino/plugins/registry.rb +75 -0
- data/lib/rubino/plugins.rb +86 -0
- data/lib/rubino/run/approval_gate.rb +243 -0
- data/lib/rubino/run/attachment_downloader.rb +166 -0
- data/lib/rubino/run/event_store.rb +74 -0
- data/lib/rubino/run/executor.rb +383 -0
- data/lib/rubino/run/gate_registry.rb +39 -0
- data/lib/rubino/run/recorder.rb +69 -0
- data/lib/rubino/run/repository.rb +118 -0
- data/lib/rubino/run/session_approval_cache.rb +118 -0
- data/lib/rubino/security/allowlist_persister.rb +55 -0
- data/lib/rubino/security/approval_policy.rb +227 -0
- data/lib/rubino/security/command_allowlist.rb +24 -0
- data/lib/rubino/security/dangerous_patterns.rb +118 -0
- data/lib/rubino/security/deny_persister.rb +73 -0
- data/lib/rubino/security/doom_loop_detector.rb +43 -0
- data/lib/rubino/security/hardline_guard.rb +105 -0
- data/lib/rubino/security/pattern_matcher.rb +62 -0
- data/lib/rubino/security/prefix_deriver.rb +124 -0
- data/lib/rubino/security/readonly_commands.rb +211 -0
- data/lib/rubino/session/exporter.rb +101 -0
- data/lib/rubino/session/message.rb +77 -0
- data/lib/rubino/session/repository.rb +295 -0
- data/lib/rubino/session/store.rb +198 -0
- data/lib/rubino/session/summary_store.rb +65 -0
- data/lib/rubino/skills/prompt_index.rb +85 -0
- data/lib/rubino/skills/registry.rb +208 -0
- data/lib/rubino/skills/skill.rb +176 -0
- data/lib/rubino/skills/skill_tool.rb +215 -0
- data/lib/rubino/skills/state_repository.rb +37 -0
- data/lib/rubino/skills/toggle.rb +26 -0
- data/lib/rubino/tools/answer_child_tool.rb +83 -0
- data/lib/rubino/tools/ask_parent_tool.rb +232 -0
- data/lib/rubino/tools/attach_file_tool.rb +120 -0
- data/lib/rubino/tools/background_tasks.rb +520 -0
- data/lib/rubino/tools/base.rb +222 -0
- data/lib/rubino/tools/custom_tool_loader.rb +119 -0
- data/lib/rubino/tools/edit_tool.rb +122 -0
- data/lib/rubino/tools/git_tool.rb +71 -0
- data/lib/rubino/tools/github_tool.rb +233 -0
- data/lib/rubino/tools/glob_tool.rb +69 -0
- data/lib/rubino/tools/grep_tool.rb +206 -0
- data/lib/rubino/tools/memory_tool.rb +184 -0
- data/lib/rubino/tools/multi_edit_tool.rb +110 -0
- data/lib/rubino/tools/patch_tool.rb +260 -0
- data/lib/rubino/tools/probe_tool.rb +175 -0
- data/lib/rubino/tools/question_tool.rb +128 -0
- data/lib/rubino/tools/read_attachment_tool.rb +180 -0
- data/lib/rubino/tools/read_tool.rb +212 -0
- data/lib/rubino/tools/read_tracker.rb +98 -0
- data/lib/rubino/tools/registry.rb +166 -0
- data/lib/rubino/tools/result.rb +113 -0
- data/lib/rubino/tools/ruby_tool.rb +0 -0
- data/lib/rubino/tools/session_search_tool.rb +103 -0
- data/lib/rubino/tools/shell_input_tool.rb +96 -0
- data/lib/rubino/tools/shell_kill_tool.rb +76 -0
- data/lib/rubino/tools/shell_output_tool.rb +72 -0
- data/lib/rubino/tools/shell_registry.rb +158 -0
- data/lib/rubino/tools/shell_tail_tool.rb +118 -0
- data/lib/rubino/tools/shell_tool.rb +330 -0
- data/lib/rubino/tools/steer_tool.rb +118 -0
- data/lib/rubino/tools/subagent_probe.rb +89 -0
- data/lib/rubino/tools/summarize_file_tool.rb +182 -0
- data/lib/rubino/tools/task_result_tool.rb +90 -0
- data/lib/rubino/tools/task_stop_tool.rb +80 -0
- data/lib/rubino/tools/task_tool.rb +622 -0
- data/lib/rubino/tools/test_tool.rb +454 -0
- data/lib/rubino/tools/todo_tool.rb +93 -0
- data/lib/rubino/tools/tool_call_repository.rb +33 -0
- data/lib/rubino/tools/vision_tool.rb +85 -0
- data/lib/rubino/tools/webfetch_tool.rb +153 -0
- data/lib/rubino/tools/websearch_tool.rb +179 -0
- data/lib/rubino/tools/write_tool.rb +61 -0
- data/lib/rubino/trust.rb +88 -0
- data/lib/rubino/ui/api.rb +296 -0
- data/lib/rubino/ui/base.rb +252 -0
- data/lib/rubino/ui/bottom_composer.rb +1599 -0
- data/lib/rubino/ui/cli.rb +1987 -0
- data/lib/rubino/ui/completion_menu.rb +321 -0
- data/lib/rubino/ui/completion_source.rb +284 -0
- data/lib/rubino/ui/escape_reader.rb +169 -0
- data/lib/rubino/ui/indented_io.rb +88 -0
- data/lib/rubino/ui/input_history.rb +108 -0
- data/lib/rubino/ui/live_region.rb +183 -0
- data/lib/rubino/ui/markdown_renderer.rb +506 -0
- data/lib/rubino/ui/notifier.rb +163 -0
- data/lib/rubino/ui/null.rb +195 -0
- data/lib/rubino/ui/paste_store.rb +176 -0
- data/lib/rubino/ui/printer_base.rb +79 -0
- data/lib/rubino/ui/probe_wait_indicator.rb +75 -0
- data/lib/rubino/ui/queued_indicators.rb +66 -0
- data/lib/rubino/ui/status_bar.rb +100 -0
- data/lib/rubino/ui/stdout_proxy.rb +161 -0
- data/lib/rubino/ui/streaming_markdown.rb +186 -0
- data/lib/rubino/ui/subagent_cards.rb +134 -0
- data/lib/rubino/ui/subagent_view.rb +255 -0
- data/lib/rubino/ui.rb +21 -0
- data/lib/rubino/update_check.rb +187 -0
- data/lib/rubino/util/duration.rb +23 -0
- data/lib/rubino/util/hyperlink.rb +105 -0
- data/lib/rubino/util/output.rb +145 -0
- data/lib/rubino/util/secrets_mask.rb +83 -0
- data/lib/rubino/version.rb +5 -0
- data/lib/rubino/workspace.rb +85 -0
- data/lib/rubino-agent.rb +5 -0
- data/lib/rubino.rb +318 -0
- data/mise.toml +2 -0
- data/rubino-agent.gemspec +103 -0
- data/skills/ruby-expert/SKILL.md +67 -0
- data/skills/ruby-expert/references/concurrency.md +357 -0
- data/skills/ruby-expert/references/datetime-and-encoding.md +363 -0
- data/skills/ruby-expert/references/errors-and-types.md +460 -0
- data/skills/ruby-expert/references/gem-authoring.md +459 -0
- data/skills/ruby-expert/references/language-idioms.md +465 -0
- data/skills/ruby-expert/references/metaprogramming.md +339 -0
- data/skills/ruby-expert/references/oo-design.md +553 -0
- data/skills/ruby-expert/references/performance.md +383 -0
- data/skills/ruby-expert/references/rails.md +424 -0
- data/skills/ruby-expert/references/security.md +404 -0
- data/skills/ruby-expert/references/testing.md +473 -0
- data/skills/ruby-expert/references/tooling.md +466 -0
- metadata +856 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# Tooling, environment & debugging
|
|
2
|
+
|
|
3
|
+
The Ruby toolchain for Ruby 3.2–3.4 and Rails 7.1–8.x. Practical commands, modern idioms, and explicit do/don't. For test frameworks see `references/testing.md`; for building/publishing your own gem see `references/gem-authoring.md`.
|
|
4
|
+
|
|
5
|
+
## Bundler
|
|
6
|
+
|
|
7
|
+
`Bundler` resolves and locks dependencies. The `Gemfile` declares them; `Gemfile.lock` pins the exact resolved versions.
|
|
8
|
+
|
|
9
|
+
### Gemfile
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
source "https://rubygems.org"
|
|
13
|
+
|
|
14
|
+
ruby file: ".ruby-version" # single source of truth; or: ruby "3.4.2"
|
|
15
|
+
|
|
16
|
+
gem "rails", "~> 8.0.1" # pessimistic: >= 8.0.1, < 8.1
|
|
17
|
+
gem "pg", "~> 1.5" # >= 1.5, < 2.0
|
|
18
|
+
gem "puma", ">= 6.0"
|
|
19
|
+
|
|
20
|
+
group :development, :test do
|
|
21
|
+
gem "debug", require: false # require: false → load only when you need it
|
|
22
|
+
gem "rspec-rails"
|
|
23
|
+
gem "factory_bot_rails"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
group :development do
|
|
27
|
+
gem "rubocop", require: false
|
|
28
|
+
gem "ruby-lsp", require: false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
group :production do
|
|
32
|
+
gem "rack-mini-profiler", require: false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
gem "nokogiri", platforms: %i[ruby]
|
|
36
|
+
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Version constraints
|
|
40
|
+
|
|
41
|
+
| Constraint | Means | Use when |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `~> 1.4.2` | `>= 1.4.2, < 1.5.0` | lock to a patch line |
|
|
44
|
+
| `~> 1.4` | `>= 1.4.0, < 2.0.0` | allow minor upgrades |
|
|
45
|
+
| `>= 1.4` | any future version | libraries, rarely apps |
|
|
46
|
+
| `1.4.2` | exact | pinning a known-good/broken version |
|
|
47
|
+
|
|
48
|
+
Prefer `~>` (pessimistic) for apps. Avoid bare `gem "foo"` with no constraint in a long-lived app — it lets a major bump slip in on a fresh `bundle install` against a deleted lock.
|
|
49
|
+
|
|
50
|
+
### Gemfile.lock commit policy
|
|
51
|
+
|
|
52
|
+
- **Applications**: ALWAYS commit `Gemfile.lock`. It guarantees every machine/CI/prod runs identical versions.
|
|
53
|
+
- **Gems/libraries**: do NOT commit `Gemfile.lock` (it's typically git-ignored). The consumer's app resolves versions. See `references/gem-authoring.md`.
|
|
54
|
+
|
|
55
|
+
### Everyday commands
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bundle install # install per the lock; resolve only new/changed gems
|
|
59
|
+
bundle install --jobs 4 # parallel
|
|
60
|
+
bundle update # re-resolve EVERYTHING to newest allowed — be careful
|
|
61
|
+
bundle update rails # update ONLY rails (and its deps) — prefer this
|
|
62
|
+
bundle update --conservative rails # update rails, hold its shared deps if possible
|
|
63
|
+
bundle outdated # list gems with newer versions available
|
|
64
|
+
bundle outdated --filter-major # only majors
|
|
65
|
+
bundle lock --add-platform x86_64-linux # add a platform for CI/Docker
|
|
66
|
+
bundle lock --update # refresh lock without installing
|
|
67
|
+
bundle clean --force # remove unused installed gem versions
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
WRONG: running `bundle update` (no args) to fix one gem — it churns the whole lock and ships unreviewed upgrades.
|
|
71
|
+
RIGHT: `bundle update <gem>` for the specific dependency, then review the lock diff.
|
|
72
|
+
|
|
73
|
+
### bundle exec, binstubs
|
|
74
|
+
|
|
75
|
+
`bundle exec <cmd>` runs a command with the gem versions from the lock, not whatever is globally installed.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bundle exec rspec
|
|
79
|
+
bundle exec rake db:migrate
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Binstubs are wrappers in `bin/` that pin the bundle so you can drop `bundle exec`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
bundle binstubs rspec-core # creates bin/rspec
|
|
86
|
+
bundle binstubs rubocop --force
|
|
87
|
+
./bin/rspec # equivalent to `bundle exec rspec`
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Rails apps ship `bin/rails`, `bin/rake`, `bin/setup`. Commit `bin/` so the team shares the same entrypoints. A `Gemfile`-aware shim is also provided by `mise`/`asdf` reshims and by `rbenv`'s rehash.
|
|
91
|
+
|
|
92
|
+
## Ruby version managers
|
|
93
|
+
|
|
94
|
+
Pick ONE per machine — mixing rbenv and rvm corrupts `PATH`. The `.ruby-version` file in the project root names the interpreter; most managers auto-switch on `cd`.
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
# .ruby-version
|
|
98
|
+
3.4.2
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Manager | Mechanism | Notes |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| **rbenv** | shims + `PATH`, rehash | lightweight, no shell hijack; `ruby-build` plugin installs versions |
|
|
104
|
+
| **rvm** | shell function overrides `cd` | heavyweight, manages gemsets; older, more invasive |
|
|
105
|
+
| **chruby** | tiny shell function + `ruby-install` | minimal, no shims, explicit |
|
|
106
|
+
| **asdf** | shims, multi-language (`.tool-versions`) | one tool for ruby+node+python |
|
|
107
|
+
| **mise** | fast Rust asdf-compatible (`.mise.toml` or `.tool-versions`) | modern default; auto-installs, env management |
|
|
108
|
+
| **rv** | new Rust Ruby manager (early/experimental) | watch but don't depend on it in prod yet |
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# rbenv
|
|
112
|
+
rbenv install 3.4.2
|
|
113
|
+
rbenv local 3.4.2 # writes .ruby-version
|
|
114
|
+
rbenv rehash # after installing a gem with a binstub
|
|
115
|
+
|
|
116
|
+
# mise
|
|
117
|
+
mise use ruby@3.4.2 # writes to .mise.toml / .tool-versions
|
|
118
|
+
mise install
|
|
119
|
+
mise exec -- ruby -v
|
|
120
|
+
|
|
121
|
+
# chruby + ruby-install
|
|
122
|
+
ruby-install ruby 3.4.2
|
|
123
|
+
chruby 3.4.2
|
|
124
|
+
|
|
125
|
+
# asdf
|
|
126
|
+
asdf install ruby 3.4.2
|
|
127
|
+
asdf local ruby 3.4.2
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
DO commit `.ruby-version` (and `.tool-versions`/`.mise.toml` if the team uses that manager). It keeps dev and CI on the same interpreter. Don't hardcode the version string in the `Gemfile` AND `.ruby-version` separately — point the Gemfile at the file: `ruby file: ".ruby-version"`.
|
|
131
|
+
|
|
132
|
+
## Debugging
|
|
133
|
+
|
|
134
|
+
### The `debug` gem (stdlib since Ruby 3.1)
|
|
135
|
+
|
|
136
|
+
`debug` is the modern, official debugger — it replaces `byebug`. Drop a breakpoint with `binding.break` (aliases: `binding.b`, `debugger`).
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
require "debug" # not needed in Rails dev; add `gem "debug"` to :development/:test
|
|
140
|
+
|
|
141
|
+
def process(order)
|
|
142
|
+
binding.break # execution stops here; opens a console
|
|
143
|
+
total = order.line_items.sum(&:price)
|
|
144
|
+
total
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
At the prompt:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
(rdbg) n # next line (step over)
|
|
152
|
+
(rdbg) s # step into
|
|
153
|
+
(rdbg) c # continue
|
|
154
|
+
(rdbg) fin # finish current frame (step out)
|
|
155
|
+
(rdbg) bt # backtrace
|
|
156
|
+
(rdbg) info # local variables
|
|
157
|
+
(rdbg) p total # evaluate Ruby
|
|
158
|
+
(rdbg) break Order#total # set a breakpoint by method
|
|
159
|
+
(rdbg) catch StandardError # break when an exception is raised
|
|
160
|
+
(rdbg) outline # methods available on the current object
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Conditional and one-shot breakpoints:
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
binding.break(do: "p user.id") # run a command then continue
|
|
167
|
+
binding.break if order.total > 1_000 # plain Ruby guard
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Remote/attach debugging (great for servers, Docker):
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
rdbg --open --port 12345 -- ruby app.rb # or: bundle exec rdbg -O ...
|
|
174
|
+
rdbg --attach 12345 # attach from another terminal
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### pry / pry-byebug
|
|
178
|
+
|
|
179
|
+
`pry` is a richer REPL; `pry-byebug` adds stepping. Still common in older codebases.
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
gem "pry-byebug", group: %i[development test]
|
|
183
|
+
binding.pry # drops into Pry; `next`/`step`/`continue`/`finish`, `ls`, `cd obj`, `show-source`
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Prefer `debug` for new code (no extra dep on modern Ruby, official, faster). Reach for Pry when you want its introspection (`ls`, `show-source`, `cd`).
|
|
187
|
+
|
|
188
|
+
### irb (modern)
|
|
189
|
+
|
|
190
|
+
Ruby 3.x ships a vastly upgraded `irb`: multiline editing, autocomplete, syntax highlighting, and a built-in debugger bridge.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
irb # autocomplete + colorized
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
irb> ls Order # list methods/constants (Pry-style)
|
|
198
|
+
irb> show_source Order#total
|
|
199
|
+
irb> edit Order#total # open in $EDITOR
|
|
200
|
+
irb> debug # hand off to the debug gem mid-session
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`bin/rails console` uses irb under the hood; `--sandbox` rolls back DB writes on exit.
|
|
204
|
+
|
|
205
|
+
### Print debugging vs a real debugger
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
puts order # → to_s, often useless for structured data
|
|
209
|
+
p order # → inspect + returns the value (chainable)
|
|
210
|
+
pp order # pretty-printed, good for nested hashes/arrays
|
|
211
|
+
warn "got #{x}" # → STDERR, won't pollute stdout
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`p` returns its argument, so you can wrap an expression without changing control flow: `total = p compute_total`.
|
|
215
|
+
|
|
216
|
+
DO use `p`/`pp` for a quick look. DON'T leave them in committed code (RuboCop's `Lint/Debugger` flags `binding.break`/`pry`; add `Rails/Output` or a custom cop for stray `puts`). For anything non-trivial — wrong values deep in a call stack, conditional state — use `binding.break` instead of sprinkling prints.
|
|
217
|
+
|
|
218
|
+
### caller / backtrace
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
puts caller # array of "file:line:in `method'" for the current stack
|
|
222
|
+
puts caller(1, 5) # skip 1 frame, take 5
|
|
223
|
+
rescue => e
|
|
224
|
+
e.backtrace.first(5) # where it was raised
|
|
225
|
+
e.full_message # message + class + backtrace, colorized
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`Thread.current.backtrace` and `caller_locations` (returns `Thread::Backtrace::Location` objects with `#path`, `#lineno`, `#label`) are useful for logging the call site without parsing strings.
|
|
229
|
+
|
|
230
|
+
### Logger
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
require "logger"
|
|
234
|
+
logger = Logger.new($stdout)
|
|
235
|
+
logger.level = Logger::INFO
|
|
236
|
+
logger.formatter = proc { |sev, time, _prog, msg| "#{time.iso8601} #{sev} #{msg}\n" }
|
|
237
|
+
|
|
238
|
+
logger.debug("query: #{sql}") # below level → suppressed
|
|
239
|
+
logger.info("processed order %d", order.id)
|
|
240
|
+
logger.warn { "expensive #{compute}" } # block form: only evaluated if level allows
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Use the block form for expensive messages so the cost is skipped when the level filters them out. In Rails use `Rails.logger` and `Rails.logger.tagged("Orders") { ... }`; configure structured/JSON logging via `config.log_formatter` or the `lograge`/semantic_logger gems. See `references/rails.md`.
|
|
244
|
+
|
|
245
|
+
## Linting & formatting
|
|
246
|
+
|
|
247
|
+
### RuboCop
|
|
248
|
+
|
|
249
|
+
Static analyzer + autocorrector. Configure with `.rubocop.yml`:
|
|
250
|
+
|
|
251
|
+
```yaml
|
|
252
|
+
# .rubocop.yml
|
|
253
|
+
require:
|
|
254
|
+
- rubocop-performance
|
|
255
|
+
plugins: # RuboCop 1.72+ prefers `plugins:` over `require:` for extensions
|
|
256
|
+
- rubocop-rails
|
|
257
|
+
- rubocop-rspec
|
|
258
|
+
|
|
259
|
+
AllCops:
|
|
260
|
+
TargetRubyVersion: 3.4
|
|
261
|
+
NewCops: enable
|
|
262
|
+
Exclude:
|
|
263
|
+
- "db/schema.rb"
|
|
264
|
+
- "vendor/**/*"
|
|
265
|
+
- "bin/**/*"
|
|
266
|
+
|
|
267
|
+
Style/Documentation:
|
|
268
|
+
Enabled: false
|
|
269
|
+
|
|
270
|
+
Metrics/MethodLength:
|
|
271
|
+
Max: 15
|
|
272
|
+
|
|
273
|
+
Layout/LineLength:
|
|
274
|
+
Max: 120
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Commands:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
bundle exec rubocop # lint
|
|
281
|
+
bundle exec rubocop -a # autocorrect SAFE cops only
|
|
282
|
+
bundle exec rubocop -A # autocorrect ALL incl. unsafe — review the diff!
|
|
283
|
+
bundle exec rubocop app/models/order.rb # one file
|
|
284
|
+
bundle exec rubocop --only Style/FrozenStringLiteralComment
|
|
285
|
+
bundle exec rubocop --format github # annotations in CI
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`-a` (safe) won't change behavior; `-A` (aggressive/unsafe) may — always re-run tests after `-A`.
|
|
289
|
+
|
|
290
|
+
### The TODO file
|
|
291
|
+
|
|
292
|
+
When adopting RuboCop on an existing codebase, generate a `.rubocop_todo.yml` that grandfathers current offenses so CI passes on day one:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
bundle exec rubocop --auto-gen-config
|
|
296
|
+
# creates .rubocop_todo.yml, inherited via inherit_from in .rubocop.yml
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Then burn it down over time. Regenerate after a big cleanup. Don't hand-disable cops globally to silence them; let the todo track the debt.
|
|
300
|
+
|
|
301
|
+
### Disabling inline (sparingly)
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
# rubocop:disable Metrics/MethodLength
|
|
305
|
+
def big_legacy_method
|
|
306
|
+
...
|
|
307
|
+
end
|
|
308
|
+
# rubocop:enable Metrics/MethodLength
|
|
309
|
+
|
|
310
|
+
result = compute # rubocop:disable Style/SomeCop -- single line, comment why
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Always re-enable, scope it as narrowly as possible, and add a `--` reason. A whole-file `# rubocop:disable all` is a smell.
|
|
314
|
+
|
|
315
|
+
### Plugins
|
|
316
|
+
|
|
317
|
+
- `rubocop-rails` — Rails-aware cops (`pluck` over `map`, `find_each`, time zones).
|
|
318
|
+
- `rubocop-rspec` — spec style (`describe` naming, `let` usage, example length).
|
|
319
|
+
- `rubocop-performance` — flags slow idioms with faster equivalents.
|
|
320
|
+
- `rubocop-rails-omakase` — Rails' own shared config (used by new Rails 8 apps).
|
|
321
|
+
|
|
322
|
+
### `standard` — zero-config alternative
|
|
323
|
+
|
|
324
|
+
The `standard` gem wraps RuboCop with a fixed, non-negotiable style (no `.rubocop.yml` bikeshedding).
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
gem "standard", group: %i[development test]
|
|
328
|
+
bundle exec standardrb # lint
|
|
329
|
+
bundle exec standardrb --fix # autocorrect
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Use `standard` when you want to end style debates; use raw RuboCop when you need fine-grained control or the Rails/RSpec/Performance cops (you can still layer `standard` + extensions). Don't run both as competing formatters on the same project.
|
|
333
|
+
|
|
334
|
+
## Editor / LSP
|
|
335
|
+
|
|
336
|
+
- **ruby-lsp** (Shopify) — the modern, actively developed language server. Fast, ships an addon API; integrates RuboCop, debugging, and indexing. Preferred default.
|
|
337
|
+
- **solargraph** — older LSP; relies on YARD docs and `.solargraph.yml`. Still works, slower indexing.
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
gem "ruby-lsp", group: :development, require: false
|
|
341
|
+
# VS Code: install the "Ruby LSP" extension; it manages the gem per-project.
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
ruby-lsp reads your `.rubocop.yml` for diagnostics/formatting and uses the bundle's gems. Add `gem "ruby-lsp-rails"` for Rails-aware features (routes, model schema). Keep these in the `Gemfile` (development group) so versions match the project, or use the global install the extension offers.
|
|
345
|
+
|
|
346
|
+
## Rake
|
|
347
|
+
|
|
348
|
+
Task runner. Define tasks in `Rakefile` or `lib/tasks/*.rake`.
|
|
349
|
+
|
|
350
|
+
```ruby
|
|
351
|
+
# lib/tasks/data.rake
|
|
352
|
+
namespace :data do
|
|
353
|
+
desc "Backfill order totals"
|
|
354
|
+
task backfill: :environment do # :environment loads Rails
|
|
355
|
+
Order.find_each { |o| o.update!(total: o.recompute_total) }
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
desc "Export, depends on backfill"
|
|
359
|
+
task export: %i[backfill] do |_t, args|
|
|
360
|
+
puts "exporting..."
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Task with arguments
|
|
365
|
+
task :greet, %i[name] => :environment do |_t, args|
|
|
366
|
+
puts "Hello #{args.name || 'world'}"
|
|
367
|
+
end
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
bundle exec rake data:backfill # run namespaced task
|
|
372
|
+
bundle exec rake "data:greet[Ada]" # pass args (quote for zsh)
|
|
373
|
+
bundle exec rake -T # list tasks with descriptions (need `desc`)
|
|
374
|
+
bundle exec rake -P # show prerequisites
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Prerequisites (`task x: :y`) run once and in dependency order. Only tasks with a `desc` show in `-T`. For anything with real logic, extract to a plain Ruby class and have the task call it — keep tasks thin and testable.
|
|
378
|
+
|
|
379
|
+
## CI — GitHub Actions matrix
|
|
380
|
+
|
|
381
|
+
```yaml
|
|
382
|
+
# .github/workflows/ci.yml
|
|
383
|
+
name: CI
|
|
384
|
+
on: [push, pull_request]
|
|
385
|
+
|
|
386
|
+
jobs:
|
|
387
|
+
test:
|
|
388
|
+
runs-on: ubuntu-latest
|
|
389
|
+
strategy:
|
|
390
|
+
fail-fast: false
|
|
391
|
+
matrix:
|
|
392
|
+
ruby: ["3.2", "3.3", "3.4"]
|
|
393
|
+
services:
|
|
394
|
+
postgres:
|
|
395
|
+
image: postgres:16
|
|
396
|
+
env: { POSTGRES_PASSWORD: postgres }
|
|
397
|
+
ports: ["5432:5432"]
|
|
398
|
+
options: >-
|
|
399
|
+
--health-cmd pg_isready --health-interval 10s
|
|
400
|
+
--health-timeout 5s --health-retries 5
|
|
401
|
+
steps:
|
|
402
|
+
- uses: actions/checkout@v4
|
|
403
|
+
- uses: ruby/setup-ruby@v1
|
|
404
|
+
with:
|
|
405
|
+
ruby-version: ${{ matrix.ruby }}
|
|
406
|
+
bundler-cache: true # runs bundle install + caches gems
|
|
407
|
+
- run: bundle exec rubocop
|
|
408
|
+
- run: bundle exec rake db:prepare
|
|
409
|
+
env: { DATABASE_URL: postgres://postgres:postgres@localhost:5432/test }
|
|
410
|
+
- run: bundle exec rspec
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`ruby/setup-ruby` with `bundler-cache: true` installs and caches gems keyed on `Gemfile.lock`. Test the Ruby versions you support (and `head` non-blocking with `continue-on-error` if you want early warning). Run lint as a separate fast job/step so style failures don't block the test signal. See `references/testing.md` for what to run.
|
|
414
|
+
|
|
415
|
+
## Environment management — dotenv
|
|
416
|
+
|
|
417
|
+
Keep secrets and per-environment config out of the repo.
|
|
418
|
+
|
|
419
|
+
```ruby
|
|
420
|
+
# Gemfile
|
|
421
|
+
gem "dotenv-rails", groups: %i[development test]
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
# .env (gitignored!) — commit a .env.example with blank/placeholder values
|
|
426
|
+
DATABASE_URL=postgres://localhost/myapp_dev
|
|
427
|
+
STRIPE_KEY=sk_test_xxx
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```ruby
|
|
431
|
+
ENV.fetch("STRIPE_KEY") # raise if missing — prefer in app code
|
|
432
|
+
ENV["STRIPE_KEY"] # nil if missing — only when truly optional
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
DO add `.env` (and `.env.local`) to `.gitignore`; commit `.env.example` documenting the keys. DON'T read raw `.env` files in production — use real env vars or Rails encrypted credentials (`bin/rails credentials:edit`, `Rails.application.credentials.stripe[:key]`). See `references/security.md` for secrets handling. Prefer `ENV.fetch("X")` over `ENV["X"]` so a missing var fails loudly at boot, not silently at runtime.
|
|
436
|
+
|
|
437
|
+
## Reading gem source
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
gem which nokogiri # path to the loaded file: .../nokogiri.rb
|
|
441
|
+
bundle show rails # install path of the bundled rails gem
|
|
442
|
+
bundle open activerecord # open the gem's source in $EDITOR (set EDITOR/BUNDLER_EDITOR)
|
|
443
|
+
bundle info rails # version, summary, path, deps
|
|
444
|
+
gem list -d rails # installed versions + details
|
|
445
|
+
gem contents rails # list files the gem installs
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
`bundle open <gem>` is the fastest way to read exactly the version your app runs (edits there persist until `bundle pristine <gem>` resets it — `bundle pristine` restores any modified gem). Use `gem which` to confirm which file/version actually loaded when there's a conflict.
|
|
449
|
+
|
|
450
|
+
## Quick checklist
|
|
451
|
+
|
|
452
|
+
- Commit `Gemfile.lock` for apps; never for libraries.
|
|
453
|
+
- Prefer `~>` pessimistic constraints; pin exact only to dodge a known bad version.
|
|
454
|
+
- `bundle update <gem>` — never bare `bundle update` to fix one dependency.
|
|
455
|
+
- Commit `.ruby-version`; point `Gemfile` at it with `ruby file: ".ruby-version"`.
|
|
456
|
+
- One version manager per machine (rbenv/mise/chruby/asdf) — don't mix.
|
|
457
|
+
- Use the `debug` gem + `binding.break` for real debugging; `p`/`pp` only for quick looks.
|
|
458
|
+
- `rubocop -a` is safe; `rubocop -A` is unsafe — re-run tests after `-A`.
|
|
459
|
+
- Adopt RuboCop via `--auto-gen-config` and burn down `.rubocop_todo.yml`; don't disable cops globally.
|
|
460
|
+
- Inline `# rubocop:disable` must be narrow, re-enabled, and justified with `--`.
|
|
461
|
+
- Use `standard` to kill style debates; raw RuboCop + plugins for fine control.
|
|
462
|
+
- ruby-lsp over solargraph for new setups.
|
|
463
|
+
- Keep Rake tasks thin; put logic in plain Ruby classes.
|
|
464
|
+
- CI: matrix the Ruby versions you support; `ruby/setup-ruby` with `bundler-cache: true`.
|
|
465
|
+
- `.env` gitignored + `.env.example` committed; `ENV.fetch` to fail loud; credentials/env vars in prod.
|
|
466
|
+
- `bundle open <gem>` / `gem which` to read the exact source you run.
|