rails-metro 0.1.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.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +17 -0
  3. data/LICENSE +21 -0
  4. data/README.md +267 -0
  5. data/exe/metro +3 -0
  6. data/lib/rails/metro/cli.rb +162 -0
  7. data/lib/rails/metro/config.rb +40 -0
  8. data/lib/rails/metro/feature_pack.rb +59 -0
  9. data/lib/rails/metro/feature_registry.rb +147 -0
  10. data/lib/rails/metro/packs/aasm_pack.rb +29 -0
  11. data/lib/rails/metro/packs/ab_testing_pack.rb +39 -0
  12. data/lib/rails/metro/packs/action_text_pack.rb +29 -0
  13. data/lib/rails/metro/packs/activeadmin_pack.rb +32 -0
  14. data/lib/rails/metro/packs/activity_feed_pack.rb +31 -0
  15. data/lib/rails/metro/packs/acts_as_list_pack.rb +28 -0
  16. data/lib/rails/metro/packs/acts_as_votable_pack.rb +32 -0
  17. data/lib/rails/metro/packs/administrate_pack.rb +31 -0
  18. data/lib/rails/metro/packs/after_party_pack.rb +31 -0
  19. data/lib/rails/metro/packs/ahoy_pack.rb +31 -0
  20. data/lib/rails/metro/packs/amplitude_pack.rb +45 -0
  21. data/lib/rails/metro/packs/annotate_pack.rb +30 -0
  22. data/lib/rails/metro/packs/api_cors_pack.rb +41 -0
  23. data/lib/rails/metro/packs/api_docs_pack.rb +34 -0
  24. data/lib/rails/metro/packs/api_guard_pack.rb +31 -0
  25. data/lib/rails/metro/packs/api_serialization_pack.rb +38 -0
  26. data/lib/rails/metro/packs/app_linting_pack.rb +35 -0
  27. data/lib/rails/metro/packs/audit_trail_pack.rb +31 -0
  28. data/lib/rails/metro/packs/authentication_pack.rb +31 -0
  29. data/lib/rails/metro/packs/authorization_pack.rb +31 -0
  30. data/lib/rails/metro/packs/avo_pack.rb +32 -0
  31. data/lib/rails/metro/packs/aws_ses_pack.rb +45 -0
  32. data/lib/rails/metro/packs/background_jobs_pack.rb +34 -0
  33. data/lib/rails/metro/packs/blazer_pack.rb +62 -0
  34. data/lib/rails/metro/packs/breadcrumbs_pack.rb +31 -0
  35. data/lib/rails/metro/packs/caching_pack.rb +30 -0
  36. data/lib/rails/metro/packs/charting_pack.rb +67 -0
  37. data/lib/rails/metro/packs/circuit_breaker_pack.rb +34 -0
  38. data/lib/rails/metro/packs/clicky_pack.rb +37 -0
  39. data/lib/rails/metro/packs/cloudinary_pack.rb +42 -0
  40. data/lib/rails/metro/packs/components_pack.rb +30 -0
  41. data/lib/rails/metro/packs/counter_culture_pack.rb +29 -0
  42. data/lib/rails/metro/packs/data_migrate_pack.rb +29 -0
  43. data/lib/rails/metro/packs/datadog_pack.rb +41 -0
  44. data/lib/rails/metro/packs/deployment_pack.rb +30 -0
  45. data/lib/rails/metro/packs/devise_pack.rb +33 -0
  46. data/lib/rails/metro/packs/doorkeeper_pack.rb +32 -0
  47. data/lib/rails/metro/packs/dotenv_pack.rb +36 -0
  48. data/lib/rails/metro/packs/elasticsearch_pack.rb +31 -0
  49. data/lib/rails/metro/packs/encryption_pack.rb +37 -0
  50. data/lib/rails/metro/packs/error_tracking_pack.rb +40 -0
  51. data/lib/rails/metro/packs/event_sourcing_pack.rb +31 -0
  52. data/lib/rails/metro/packs/faraday_pack.rb +42 -0
  53. data/lib/rails/metro/packs/fathom_pack.rb +36 -0
  54. data/lib/rails/metro/packs/feature_flags_pack.rb +34 -0
  55. data/lib/rails/metro/packs/friendly_id_pack.rb +31 -0
  56. data/lib/rails/metro/packs/geocoder_pack.rb +38 -0
  57. data/lib/rails/metro/packs/good_job_pack.rb +34 -0
  58. data/lib/rails/metro/packs/google_analytics_pack.rb +42 -0
  59. data/lib/rails/metro/packs/google_tag_manager_pack.rb +51 -0
  60. data/lib/rails/metro/packs/graphql_pack.rb +31 -0
  61. data/lib/rails/metro/packs/health_check_pack.rb +34 -0
  62. data/lib/rails/metro/packs/heap_pack.rb +39 -0
  63. data/lib/rails/metro/packs/honeybadger_pack.rb +37 -0
  64. data/lib/rails/metro/packs/hotwire_livereload_pack.rb +28 -0
  65. data/lib/rails/metro/packs/icons_pack.rb +28 -0
  66. data/lib/rails/metro/packs/invisible_captcha_pack.rb +35 -0
  67. data/lib/rails/metro/packs/kredis_pack.rb +31 -0
  68. data/lib/rails/metro/packs/lemon_squeezy_pack.rb +36 -0
  69. data/lib/rails/metro/packs/letter_opener_pack.rb +31 -0
  70. data/lib/rails/metro/packs/letter_opener_web_pack.rb +32 -0
  71. data/lib/rails/metro/packs/logging_pack.rb +36 -0
  72. data/lib/rails/metro/packs/madmin_pack.rb +32 -0
  73. data/lib/rails/metro/packs/mailgun_pack.rb +44 -0
  74. data/lib/rails/metro/packs/maintenance_mode_pack.rb +30 -0
  75. data/lib/rails/metro/packs/maintenance_tasks_pack.rb +32 -0
  76. data/lib/rails/metro/packs/markdown_pack.rb +48 -0
  77. data/lib/rails/metro/packs/matomo_pack.rb +48 -0
  78. data/lib/rails/metro/packs/meilisearch_pack.rb +37 -0
  79. data/lib/rails/metro/packs/mixpanel_pack.rb +47 -0
  80. data/lib/rails/metro/packs/mobility_pack.rb +32 -0
  81. data/lib/rails/metro/packs/mollie_pack.rb +36 -0
  82. data/lib/rails/metro/packs/multitenancy_pack.rb +35 -0
  83. data/lib/rails/metro/packs/newrelic_pack.rb +52 -0
  84. data/lib/rails/metro/packs/notifications_pack.rb +31 -0
  85. data/lib/rails/metro/packs/omniauth_pack.rb +49 -0
  86. data/lib/rails/metro/packs/paddle_pack.rb +37 -0
  87. data/lib/rails/metro/packs/pagination_pack.rb +44 -0
  88. data/lib/rails/metro/packs/passwordless_pack.rb +32 -0
  89. data/lib/rails/metro/packs/payments_pack.rb +35 -0
  90. data/lib/rails/metro/packs/paypal_pack.rb +35 -0
  91. data/lib/rails/metro/packs/pdf_pack.rb +45 -0
  92. data/lib/rails/metro/packs/performance_pack.rb +37 -0
  93. data/lib/rails/metro/packs/pghero_pack.rb +31 -0
  94. data/lib/rails/metro/packs/plausible_pack.rb +36 -0
  95. data/lib/rails/metro/packs/posthog_pack.rb +40 -0
  96. data/lib/rails/metro/packs/postmark_pack.rb +39 -0
  97. data/lib/rails/metro/packs/pretender_pack.rb +33 -0
  98. data/lib/rails/metro/packs/profiling_pack.rb +30 -0
  99. data/lib/rails/metro/packs/pundit_pack.rb +50 -0
  100. data/lib/rails/metro/packs/qr_code_pack.rb +37 -0
  101. data/lib/rails/metro/packs/r2_storage_pack.rb +48 -0
  102. data/lib/rails/metro/packs/rack_timeout_pack.rb +34 -0
  103. data/lib/rails/metro/packs/rails_i18n_pack.rb +31 -0
  104. data/lib/rails/metro/packs/ransack_pack.rb +29 -0
  105. data/lib/rails/metro/packs/rate_limiting_pack.rb +39 -0
  106. data/lib/rails/metro/packs/recaptcha_pack.rb +38 -0
  107. data/lib/rails/metro/packs/resend_pack.rb +37 -0
  108. data/lib/rails/metro/packs/revenuecat_pack.rb +36 -0
  109. data/lib/rails/metro/packs/rolify_pack.rb +32 -0
  110. data/lib/rails/metro/packs/rollbar_pack.rb +38 -0
  111. data/lib/rails/metro/packs/s3_storage_pack.rb +45 -0
  112. data/lib/rails/metro/packs/scenic_pack.rb +28 -0
  113. data/lib/rails/metro/packs/scheduling_pack.rb +46 -0
  114. data/lib/rails/metro/packs/search_pack.rb +32 -0
  115. data/lib/rails/metro/packs/security_pack.rb +30 -0
  116. data/lib/rails/metro/packs/sendgrid_pack.rb +44 -0
  117. data/lib/rails/metro/packs/sent_dm_pack.rb +36 -0
  118. data/lib/rails/metro/packs/seo_pack.rb +40 -0
  119. data/lib/rails/metro/packs/shoulda_matchers_pack.rb +37 -0
  120. data/lib/rails/metro/packs/sidekiq_pack.rb +42 -0
  121. data/lib/rails/metro/packs/simple_calendar_pack.rb +29 -0
  122. data/lib/rails/metro/packs/simple_form_pack.rb +30 -0
  123. data/lib/rails/metro/packs/simplecov_pack.rb +38 -0
  124. data/lib/rails/metro/packs/slack_notifier_pack.rb +36 -0
  125. data/lib/rails/metro/packs/soft_deletes_pack.rb +30 -0
  126. data/lib/rails/metro/packs/solidus_pack.rb +31 -0
  127. data/lib/rails/metro/packs/spreadsheets_pack.rb +29 -0
  128. data/lib/rails/metro/packs/statcounter_pack.rb +41 -0
  129. data/lib/rails/metro/packs/stimulus_components_pack.rb +33 -0
  130. data/lib/rails/metro/packs/storage_validations_pack.rb +28 -0
  131. data/lib/rails/metro/packs/strong_migrations_pack.rb +30 -0
  132. data/lib/rails/metro/packs/tagging_pack.rb +31 -0
  133. data/lib/rails/metro/packs/test_mocking_pack.rb +41 -0
  134. data/lib/rails/metro/packs/testing_pack.rb +39 -0
  135. data/lib/rails/metro/packs/twilio_pack.rb +38 -0
  136. data/lib/rails/metro/packs/vite_rails_pack.rb +31 -0
  137. data/lib/rails/metro/packs/web_push_pack.rb +39 -0
  138. data/lib/rails/metro/packs/webhooks_pack.rb +45 -0
  139. data/lib/rails/metro/packs/websockets_pack.rb +29 -0
  140. data/lib/rails/metro/template_compiler.rb +85 -0
  141. data/lib/rails/metro/tui/app.rb +119 -0
  142. data/lib/rails/metro/tui/steps/app_name_step.rb +57 -0
  143. data/lib/rails/metro/tui/steps/database_step.rb +53 -0
  144. data/lib/rails/metro/tui/steps/pack_step.rb +248 -0
  145. data/lib/rails/metro/tui/steps/review_step.rb +94 -0
  146. data/lib/rails/metro/tui/styles.rb +47 -0
  147. data/lib/rails/metro/tui.rb +37 -0
  148. data/lib/rails/metro/version.rb +5 -0
  149. data/lib/rails/metro.rb +9 -0
  150. metadata +206 -0
@@ -0,0 +1,39 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class PostmarkPack < FeaturePack
5
+ pack_name "postmark"
6
+ description "Postmark for fast transactional email (99%+ inbox delivery)"
7
+ category "notifications"
8
+
9
+ def gems
10
+ [
11
+ {name: "postmark-rails"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'create_file "config/initializers/postmark.rb", <<~RUBY',
18
+ " ActionMailer::Base.delivery_method = :postmark",
19
+ " ActionMailer::Base.postmark_settings = {",
20
+ ' api_token: Rails.application.credentials.dig(:postmark, :api_token) || ENV["POSTMARK_API_TOKEN"]',
21
+ " }",
22
+ "RUBY",
23
+ "",
24
+ 'environment "config.action_mailer.delivery_method = :postmark", env: "production"'
25
+ ]
26
+ end
27
+
28
+ def post_install_notes
29
+ [
30
+ "Postmark: Add your API token with `bin/rails credentials:edit`:",
31
+ " postmark:",
32
+ " api_token: your_server_api_token",
33
+ "Postmark: Known for excellent deliverability and fast delivery times"
34
+ ]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class PretenderPack < FeaturePack
5
+ pack_name "pretender"
6
+ description "Pretender for user impersonation (admin support tool)"
7
+ category "ops"
8
+
9
+ def gems
10
+ [
11
+ {name: "pretender"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'inject_into_file "app/controllers/application_controller.rb", after: "class ApplicationController < ActionController::Base\\n" do',
18
+ ' " impersonates :user\\n"',
19
+ "end"
20
+ ]
21
+ end
22
+
23
+ def post_install_notes
24
+ [
25
+ "Pretender: Impersonate with `impersonate_user(user)` in a controller action",
26
+ "Pretender: Stop with `stop_impersonating_user`",
27
+ "Pretender: `true_user` returns the admin, `current_user` returns the impersonated user"
28
+ ]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class ProfilingPack < FeaturePack
5
+ pack_name "profiling"
6
+ description "Stackprof + memory_profiler for CPU and memory profiling"
7
+ category "ops"
8
+
9
+ def gems
10
+ [
11
+ {name: "stackprof", group: :development},
12
+ {name: "memory_profiler", group: :development}
13
+ ]
14
+ end
15
+
16
+ def template_lines
17
+ []
18
+ end
19
+
20
+ def post_install_notes
21
+ [
22
+ "Profiling: CPU profile with `StackProf.run(mode: :cpu) { code }` and `StackProf::Report`",
23
+ "Profiling: Memory profile with `MemoryProfiler.report { code }.pretty_print`",
24
+ "Profiling: Both available in development only"
25
+ ]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,50 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class PunditPack < FeaturePack
5
+ pack_name "pundit"
6
+ description "Pundit for simple, policy-based authorization"
7
+ category "core"
8
+ conflicts_with "authorization"
9
+
10
+ def gems
11
+ [
12
+ {name: "pundit"}
13
+ ]
14
+ end
15
+
16
+ def template_lines
17
+ [
18
+ 'inject_into_file "app/controllers/application_controller.rb", after: "class ApplicationController < ActionController::Base\\n" do',
19
+ ' " include Pundit::Authorization\\n"',
20
+ "end",
21
+ "",
22
+ 'create_file "app/policies/application_policy.rb", <<~RUBY',
23
+ " class ApplicationPolicy",
24
+ " attr_reader :user, :record",
25
+ "",
26
+ " def initialize(user, record)",
27
+ " @user = user",
28
+ " @record = record",
29
+ " end",
30
+ "",
31
+ " def index? = false",
32
+ " def show? = false",
33
+ " def create? = false",
34
+ " def update? = false",
35
+ " def destroy? = false",
36
+ " end",
37
+ "RUBY"
38
+ ]
39
+ end
40
+
41
+ def post_install_notes
42
+ [
43
+ "Pundit: Generate policies with `bin/rails g pundit:policy User`",
44
+ "Pundit: Use `authorize @user` in controllers"
45
+ ]
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class QrCodePack < FeaturePack
5
+ pack_name "qr_code"
6
+ description "RQRCode for QR code generation (SVG, PNG, ANSI)"
7
+ category "ui"
8
+
9
+ def gems
10
+ [
11
+ {name: "rqrcode"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'create_file "app/helpers/qr_code_helper.rb", <<~RUBY',
18
+ " module QrCodeHelper",
19
+ ' def qr_code_svg(content, size: 4, color: "000")',
20
+ " qr = RQRCode::QRCode.new(content)",
21
+ ' qr.as_svg(module_size: size, color: color, shape_rendering: "crispEdges").html_safe',
22
+ " end",
23
+ " end",
24
+ "RUBY"
25
+ ]
26
+ end
27
+
28
+ def post_install_notes
29
+ [
30
+ "QR Code: Use `<%= qr_code_svg(\"https://example.com\") %>` in views",
31
+ "QR Code: Also supports PNG with `qr.as_png` and ANSI terminal output"
32
+ ]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class R2StoragePack < FeaturePack
5
+ pack_name "r2_storage"
6
+ description "Cloudflare R2 for S3-compatible Active Storage (no egress fees)"
7
+ category "core"
8
+ conflicts_with "s3_storage"
9
+
10
+ def gems
11
+ [
12
+ {name: "aws-sdk-s3"}
13
+ ]
14
+ end
15
+
16
+ def template_lines
17
+ [
18
+ 'inject_into_file "config/storage.yml", <<~YML',
19
+ "",
20
+ " r2:",
21
+ " service: S3",
22
+ " endpoint: <%= Rails.application.credentials.dig(:r2, :endpoint) %>",
23
+ ' region: <%= Rails.application.credentials.dig(:r2, :region) || "auto" %>',
24
+ " bucket: <%= Rails.application.credentials.dig(:r2, :bucket) %>",
25
+ " access_key_id: <%= Rails.application.credentials.dig(:r2, :access_key_id) %>",
26
+ " secret_access_key: <%= Rails.application.credentials.dig(:r2, :secret_access_key) %>",
27
+ "YML",
28
+ "",
29
+ 'environment "config.active_storage.service = :r2", env: "production"'
30
+ ]
31
+ end
32
+
33
+ def post_install_notes
34
+ [
35
+ "R2: Add your Cloudflare credentials with `bin/rails credentials:edit`:",
36
+ " r2:",
37
+ " endpoint: https://ACCOUNT_ID.r2.cloudflarestorage.com",
38
+ " region: auto",
39
+ " bucket: your-bucket-name",
40
+ " access_key_id: your_access_key",
41
+ " secret_access_key: your_secret_key",
42
+ "R2: Zero egress fees -- great for media-heavy apps"
43
+ ]
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,34 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RackTimeoutPack < FeaturePack
5
+ pack_name "rack_timeout"
6
+ description "Rack::Timeout for request timeout protection"
7
+ category "ops"
8
+
9
+ def gems
10
+ [
11
+ {name: "rack-timeout"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'create_file "config/initializers/rack_timeout.rb", <<~RUBY',
18
+ " Rack::Timeout.service_timeout = 15",
19
+ " Rack::Timeout.wait_timeout = 30",
20
+ " Rack::Timeout.wait_overtime = 60",
21
+ "RUBY"
22
+ ]
23
+ end
24
+
25
+ def post_install_notes
26
+ [
27
+ "Rack::Timeout: Requests exceeding 15s are terminated with a Rack::Timeout::RequestTimeoutError",
28
+ "Rack::Timeout: Customize thresholds in config/initializers/rack_timeout.rb"
29
+ ]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RailsI18nPack < FeaturePack
5
+ pack_name "rails_i18n"
6
+ description "Rails I18n locale data for 100+ languages"
7
+ category "core"
8
+
9
+ def gems
10
+ [
11
+ {name: "rails-i18n"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'environment "config.i18n.available_locales = [:en]',
18
+ ' config.i18n.default_locale = :en"'
19
+ ]
20
+ end
21
+
22
+ def post_install_notes
23
+ [
24
+ "I18n: Add locales to config.i18n.available_locales as needed",
25
+ "I18n: Locale files available for 100+ languages (date formats, pluralization, etc.)"
26
+ ]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RansackPack < FeaturePack
5
+ pack_name "ransack"
6
+ description "Ransack for object-based search and filter forms"
7
+ category "ui"
8
+
9
+ def gems
10
+ [
11
+ {name: "ransack", version: "~> 4.0"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ []
17
+ end
18
+
19
+ def post_install_notes
20
+ [
21
+ "Ransack: Use `@q = User.ransack(params[:q])` in controllers",
22
+ "Ransack: Build forms with `search_form_for @q do |f|` in views",
23
+ "Ransack: Supports sorting, predicates (_cont, _eq, _gt, etc.)"
24
+ ]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RateLimitingPack < FeaturePack
5
+ pack_name "rate_limiting"
6
+ description "Rack::Attack for throttling and blocking"
7
+ category "ops"
8
+
9
+ def gems
10
+ [
11
+ {name: "rack-attack"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'create_file "config/initializers/rack_attack.rb", <<~RUBY',
18
+ " class Rack::Attack",
19
+ ' throttle("req/ip", limit: 300, period: 5.minutes) do |req|',
20
+ " req.ip",
21
+ " end",
22
+ "",
23
+ ' throttle("logins/ip", limit: 5, period: 20.seconds) do |req|',
24
+ ' req.ip if req.path == "/session" && req.post?',
25
+ " end",
26
+ " end",
27
+ "RUBY"
28
+ ]
29
+ end
30
+
31
+ def post_install_notes
32
+ [
33
+ "Rate Limiting: Customize throttle rules in config/initializers/rack_attack.rb"
34
+ ]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RecaptchaPack < FeaturePack
5
+ pack_name "recaptcha"
6
+ description "reCAPTCHA v3 for Google-powered bot protection"
7
+ category "core"
8
+
9
+ def gems
10
+ [
11
+ {name: "recaptcha"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'create_file "config/initializers/recaptcha.rb", <<~RUBY',
18
+ " Recaptcha.configure do |config|",
19
+ ' config.site_key = Rails.application.credentials.dig(:recaptcha, :site_key) || ENV["RECAPTCHA_SITE_KEY"]',
20
+ ' config.secret_key = Rails.application.credentials.dig(:recaptcha, :secret_key) || ENV["RECAPTCHA_SECRET_KEY"]',
21
+ " end",
22
+ "RUBY"
23
+ ]
24
+ end
25
+
26
+ def post_install_notes
27
+ [
28
+ "reCAPTCHA: Add your keys with `bin/rails credentials:edit`:",
29
+ " recaptcha:",
30
+ " site_key: your_site_key",
31
+ " secret_key: your_secret_key",
32
+ "reCAPTCHA: Use `<%= recaptcha_v3 %>` in forms and `verify_recaptcha` in controllers"
33
+ ]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class ResendPack < FeaturePack
5
+ pack_name "resend"
6
+ description "Resend for transactional and marketing email delivery"
7
+ category "notifications"
8
+
9
+ def gems
10
+ [
11
+ {name: "resend"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'create_file "config/initializers/resend.rb", <<~RUBY',
18
+ " Resend.api_key = Rails.application.credentials.dig(:resend, :api_key) || ENV[\"RESEND_API_KEY\"]",
19
+ "RUBY",
20
+ "",
21
+ "# Configure Action Mailer to use Resend",
22
+ 'environment "config.action_mailer.delivery_method = :resend", env: "production"'
23
+ ]
24
+ end
25
+
26
+ def post_install_notes
27
+ [
28
+ "Resend: Add your API key with `bin/rails credentials:edit`:",
29
+ " resend:",
30
+ " api_key: re_your_api_key_here",
31
+ "Resend: Verify your sending domain at https://resend.com/domains"
32
+ ]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RevenuecatPack < FeaturePack
5
+ pack_name "revenuecat"
6
+ description "RevenueCat for subscription management and in-app purchases"
7
+ category "payments"
8
+
9
+ def gems
10
+ [
11
+ {name: "revenuecat-api"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'create_file "config/initializers/revenuecat.rb", <<~RUBY',
18
+ " RevenueCat.configure do |config|",
19
+ ' config.api_key = Rails.application.credentials.dig(:revenuecat, :api_key) || ENV["REVENUECAT_API_KEY"]',
20
+ " end",
21
+ "RUBY"
22
+ ]
23
+ end
24
+
25
+ def post_install_notes
26
+ [
27
+ "RevenueCat: Add your API key with `bin/rails credentials:edit`:",
28
+ " revenuecat:",
29
+ " api_key: your_api_key_here",
30
+ "RevenueCat: Best suited for apps with mobile (iOS/Android) subscription billing"
31
+ ]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RolifyPack < FeaturePack
5
+ pack_name "rolify"
6
+ description "Rolify for role management (admin, editor, viewer, etc.)"
7
+ category "core"
8
+
9
+ def gems
10
+ [
11
+ {name: "rolify"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ [
17
+ 'rails_command "generate rolify Role User"'
18
+ ]
19
+ end
20
+
21
+ def post_install_notes
22
+ [
23
+ "Rolify: Run `bin/rails db:migrate` to create roles table",
24
+ "Rolify: Assign with `user.add_role :admin`",
25
+ "Rolify: Check with `user.has_role? :admin`",
26
+ "Rolify: Pairs well with Pundit or Action Policy for authorization"
27
+ ]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class RollbarPack < FeaturePack
5
+ pack_name "rollbar"
6
+ description "Rollbar for real-time error tracking and debugging"
7
+ category "ops"
8
+ conflicts_with "error_tracking", "honeybadger"
9
+
10
+ def gems
11
+ [
12
+ {name: "rollbar"}
13
+ ]
14
+ end
15
+
16
+ def template_lines
17
+ [
18
+ 'create_file "config/initializers/rollbar.rb", <<~RUBY',
19
+ " Rollbar.configure do |config|",
20
+ ' config.access_token = Rails.application.credentials.dig(:rollbar, :access_token) || ENV["ROLLBAR_ACCESS_TOKEN"]',
21
+ " config.enabled = !Rails.env.test?",
22
+ " config.environment = Rails.env",
23
+ " end",
24
+ "RUBY"
25
+ ]
26
+ end
27
+
28
+ def post_install_notes
29
+ [
30
+ "Rollbar: Add your access token with `bin/rails credentials:edit`:",
31
+ " rollbar:",
32
+ " access_token: your_token_here"
33
+ ]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,45 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class S3StoragePack < FeaturePack
5
+ pack_name "s3_storage"
6
+ description "AWS S3 for Active Storage file uploads"
7
+ category "core"
8
+ conflicts_with "r2_storage"
9
+
10
+ def gems
11
+ [
12
+ {name: "aws-sdk-s3"}
13
+ ]
14
+ end
15
+
16
+ def template_lines
17
+ [
18
+ 'inject_into_file "config/storage.yml", <<~YML',
19
+ "",
20
+ " amazon:",
21
+ " service: S3",
22
+ " access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>",
23
+ " secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>",
24
+ ' region: <%= Rails.application.credentials.dig(:aws, :region) || "us-east-1" %>',
25
+ " bucket: <%= Rails.application.credentials.dig(:aws, :bucket) %>",
26
+ "YML",
27
+ "",
28
+ 'environment "config.active_storage.service = :amazon", env: "production"'
29
+ ]
30
+ end
31
+
32
+ def post_install_notes
33
+ [
34
+ "S3: Add your AWS credentials with `bin/rails credentials:edit`:",
35
+ " aws:",
36
+ " access_key_id: AKIA...",
37
+ " secret_access_key: your_secret",
38
+ " region: us-east-1",
39
+ " bucket: your-bucket-name"
40
+ ]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class ScenicPack < FeaturePack
5
+ pack_name "scenic"
6
+ description "Scenic for versioned database views"
7
+ category "data"
8
+
9
+ def gems
10
+ [
11
+ {name: "scenic"}
12
+ ]
13
+ end
14
+
15
+ def template_lines
16
+ []
17
+ end
18
+
19
+ def post_install_notes
20
+ [
21
+ "Scenic: Generate views with `bin/rails g scenic:view search_results`",
22
+ "Scenic: Views are versioned -- update with `bin/rails g scenic:view search_results --replace`"
23
+ ]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ module Rails
2
+ module Metro
3
+ module Packs
4
+ class SchedulingPack < FeaturePack
5
+ pack_name "scheduling"
6
+ description "Recurring tasks with Solid Queue and solid_queue recurring"
7
+ category "core"
8
+ depends_on "background_jobs"
9
+
10
+ def gems
11
+ []
12
+ end
13
+
14
+ def template_lines
15
+ [
16
+ 'create_file "config/recurring.yml", <<~YML',
17
+ " production:",
18
+ " cleanup_old_records:",
19
+ " class: CleanupOldRecordsJob",
20
+ ' schedule: "every day at 3am"',
21
+ " queue: default",
22
+ "YML",
23
+ "",
24
+ 'create_file "app/jobs/cleanup_old_records_job.rb", <<~RUBY',
25
+ " class CleanupOldRecordsJob < ApplicationJob",
26
+ " queue_as :default",
27
+ "",
28
+ " def perform",
29
+ " # Add your recurring cleanup logic here",
30
+ ' Rails.logger.info "Running scheduled cleanup"',
31
+ " end",
32
+ " end",
33
+ "RUBY"
34
+ ]
35
+ end
36
+
37
+ def post_install_notes
38
+ [
39
+ "Scheduling: Define recurring jobs in config/recurring.yml",
40
+ "Scheduling: Solid Queue handles scheduling natively -- no cron or external gems needed"
41
+ ]
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end