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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/LICENSE +21 -0
- data/README.md +267 -0
- data/exe/metro +3 -0
- data/lib/rails/metro/cli.rb +162 -0
- data/lib/rails/metro/config.rb +40 -0
- data/lib/rails/metro/feature_pack.rb +59 -0
- data/lib/rails/metro/feature_registry.rb +147 -0
- data/lib/rails/metro/packs/aasm_pack.rb +29 -0
- data/lib/rails/metro/packs/ab_testing_pack.rb +39 -0
- data/lib/rails/metro/packs/action_text_pack.rb +29 -0
- data/lib/rails/metro/packs/activeadmin_pack.rb +32 -0
- data/lib/rails/metro/packs/activity_feed_pack.rb +31 -0
- data/lib/rails/metro/packs/acts_as_list_pack.rb +28 -0
- data/lib/rails/metro/packs/acts_as_votable_pack.rb +32 -0
- data/lib/rails/metro/packs/administrate_pack.rb +31 -0
- data/lib/rails/metro/packs/after_party_pack.rb +31 -0
- data/lib/rails/metro/packs/ahoy_pack.rb +31 -0
- data/lib/rails/metro/packs/amplitude_pack.rb +45 -0
- data/lib/rails/metro/packs/annotate_pack.rb +30 -0
- data/lib/rails/metro/packs/api_cors_pack.rb +41 -0
- data/lib/rails/metro/packs/api_docs_pack.rb +34 -0
- data/lib/rails/metro/packs/api_guard_pack.rb +31 -0
- data/lib/rails/metro/packs/api_serialization_pack.rb +38 -0
- data/lib/rails/metro/packs/app_linting_pack.rb +35 -0
- data/lib/rails/metro/packs/audit_trail_pack.rb +31 -0
- data/lib/rails/metro/packs/authentication_pack.rb +31 -0
- data/lib/rails/metro/packs/authorization_pack.rb +31 -0
- data/lib/rails/metro/packs/avo_pack.rb +32 -0
- data/lib/rails/metro/packs/aws_ses_pack.rb +45 -0
- data/lib/rails/metro/packs/background_jobs_pack.rb +34 -0
- data/lib/rails/metro/packs/blazer_pack.rb +62 -0
- data/lib/rails/metro/packs/breadcrumbs_pack.rb +31 -0
- data/lib/rails/metro/packs/caching_pack.rb +30 -0
- data/lib/rails/metro/packs/charting_pack.rb +67 -0
- data/lib/rails/metro/packs/circuit_breaker_pack.rb +34 -0
- data/lib/rails/metro/packs/clicky_pack.rb +37 -0
- data/lib/rails/metro/packs/cloudinary_pack.rb +42 -0
- data/lib/rails/metro/packs/components_pack.rb +30 -0
- data/lib/rails/metro/packs/counter_culture_pack.rb +29 -0
- data/lib/rails/metro/packs/data_migrate_pack.rb +29 -0
- data/lib/rails/metro/packs/datadog_pack.rb +41 -0
- data/lib/rails/metro/packs/deployment_pack.rb +30 -0
- data/lib/rails/metro/packs/devise_pack.rb +33 -0
- data/lib/rails/metro/packs/doorkeeper_pack.rb +32 -0
- data/lib/rails/metro/packs/dotenv_pack.rb +36 -0
- data/lib/rails/metro/packs/elasticsearch_pack.rb +31 -0
- data/lib/rails/metro/packs/encryption_pack.rb +37 -0
- data/lib/rails/metro/packs/error_tracking_pack.rb +40 -0
- data/lib/rails/metro/packs/event_sourcing_pack.rb +31 -0
- data/lib/rails/metro/packs/faraday_pack.rb +42 -0
- data/lib/rails/metro/packs/fathom_pack.rb +36 -0
- data/lib/rails/metro/packs/feature_flags_pack.rb +34 -0
- data/lib/rails/metro/packs/friendly_id_pack.rb +31 -0
- data/lib/rails/metro/packs/geocoder_pack.rb +38 -0
- data/lib/rails/metro/packs/good_job_pack.rb +34 -0
- data/lib/rails/metro/packs/google_analytics_pack.rb +42 -0
- data/lib/rails/metro/packs/google_tag_manager_pack.rb +51 -0
- data/lib/rails/metro/packs/graphql_pack.rb +31 -0
- data/lib/rails/metro/packs/health_check_pack.rb +34 -0
- data/lib/rails/metro/packs/heap_pack.rb +39 -0
- data/lib/rails/metro/packs/honeybadger_pack.rb +37 -0
- data/lib/rails/metro/packs/hotwire_livereload_pack.rb +28 -0
- data/lib/rails/metro/packs/icons_pack.rb +28 -0
- data/lib/rails/metro/packs/invisible_captcha_pack.rb +35 -0
- data/lib/rails/metro/packs/kredis_pack.rb +31 -0
- data/lib/rails/metro/packs/lemon_squeezy_pack.rb +36 -0
- data/lib/rails/metro/packs/letter_opener_pack.rb +31 -0
- data/lib/rails/metro/packs/letter_opener_web_pack.rb +32 -0
- data/lib/rails/metro/packs/logging_pack.rb +36 -0
- data/lib/rails/metro/packs/madmin_pack.rb +32 -0
- data/lib/rails/metro/packs/mailgun_pack.rb +44 -0
- data/lib/rails/metro/packs/maintenance_mode_pack.rb +30 -0
- data/lib/rails/metro/packs/maintenance_tasks_pack.rb +32 -0
- data/lib/rails/metro/packs/markdown_pack.rb +48 -0
- data/lib/rails/metro/packs/matomo_pack.rb +48 -0
- data/lib/rails/metro/packs/meilisearch_pack.rb +37 -0
- data/lib/rails/metro/packs/mixpanel_pack.rb +47 -0
- data/lib/rails/metro/packs/mobility_pack.rb +32 -0
- data/lib/rails/metro/packs/mollie_pack.rb +36 -0
- data/lib/rails/metro/packs/multitenancy_pack.rb +35 -0
- data/lib/rails/metro/packs/newrelic_pack.rb +52 -0
- data/lib/rails/metro/packs/notifications_pack.rb +31 -0
- data/lib/rails/metro/packs/omniauth_pack.rb +49 -0
- data/lib/rails/metro/packs/paddle_pack.rb +37 -0
- data/lib/rails/metro/packs/pagination_pack.rb +44 -0
- data/lib/rails/metro/packs/passwordless_pack.rb +32 -0
- data/lib/rails/metro/packs/payments_pack.rb +35 -0
- data/lib/rails/metro/packs/paypal_pack.rb +35 -0
- data/lib/rails/metro/packs/pdf_pack.rb +45 -0
- data/lib/rails/metro/packs/performance_pack.rb +37 -0
- data/lib/rails/metro/packs/pghero_pack.rb +31 -0
- data/lib/rails/metro/packs/plausible_pack.rb +36 -0
- data/lib/rails/metro/packs/posthog_pack.rb +40 -0
- data/lib/rails/metro/packs/postmark_pack.rb +39 -0
- data/lib/rails/metro/packs/pretender_pack.rb +33 -0
- data/lib/rails/metro/packs/profiling_pack.rb +30 -0
- data/lib/rails/metro/packs/pundit_pack.rb +50 -0
- data/lib/rails/metro/packs/qr_code_pack.rb +37 -0
- data/lib/rails/metro/packs/r2_storage_pack.rb +48 -0
- data/lib/rails/metro/packs/rack_timeout_pack.rb +34 -0
- data/lib/rails/metro/packs/rails_i18n_pack.rb +31 -0
- data/lib/rails/metro/packs/ransack_pack.rb +29 -0
- data/lib/rails/metro/packs/rate_limiting_pack.rb +39 -0
- data/lib/rails/metro/packs/recaptcha_pack.rb +38 -0
- data/lib/rails/metro/packs/resend_pack.rb +37 -0
- data/lib/rails/metro/packs/revenuecat_pack.rb +36 -0
- data/lib/rails/metro/packs/rolify_pack.rb +32 -0
- data/lib/rails/metro/packs/rollbar_pack.rb +38 -0
- data/lib/rails/metro/packs/s3_storage_pack.rb +45 -0
- data/lib/rails/metro/packs/scenic_pack.rb +28 -0
- data/lib/rails/metro/packs/scheduling_pack.rb +46 -0
- data/lib/rails/metro/packs/search_pack.rb +32 -0
- data/lib/rails/metro/packs/security_pack.rb +30 -0
- data/lib/rails/metro/packs/sendgrid_pack.rb +44 -0
- data/lib/rails/metro/packs/sent_dm_pack.rb +36 -0
- data/lib/rails/metro/packs/seo_pack.rb +40 -0
- data/lib/rails/metro/packs/shoulda_matchers_pack.rb +37 -0
- data/lib/rails/metro/packs/sidekiq_pack.rb +42 -0
- data/lib/rails/metro/packs/simple_calendar_pack.rb +29 -0
- data/lib/rails/metro/packs/simple_form_pack.rb +30 -0
- data/lib/rails/metro/packs/simplecov_pack.rb +38 -0
- data/lib/rails/metro/packs/slack_notifier_pack.rb +36 -0
- data/lib/rails/metro/packs/soft_deletes_pack.rb +30 -0
- data/lib/rails/metro/packs/solidus_pack.rb +31 -0
- data/lib/rails/metro/packs/spreadsheets_pack.rb +29 -0
- data/lib/rails/metro/packs/statcounter_pack.rb +41 -0
- data/lib/rails/metro/packs/stimulus_components_pack.rb +33 -0
- data/lib/rails/metro/packs/storage_validations_pack.rb +28 -0
- data/lib/rails/metro/packs/strong_migrations_pack.rb +30 -0
- data/lib/rails/metro/packs/tagging_pack.rb +31 -0
- data/lib/rails/metro/packs/test_mocking_pack.rb +41 -0
- data/lib/rails/metro/packs/testing_pack.rb +39 -0
- data/lib/rails/metro/packs/twilio_pack.rb +38 -0
- data/lib/rails/metro/packs/vite_rails_pack.rb +31 -0
- data/lib/rails/metro/packs/web_push_pack.rb +39 -0
- data/lib/rails/metro/packs/webhooks_pack.rb +45 -0
- data/lib/rails/metro/packs/websockets_pack.rb +29 -0
- data/lib/rails/metro/template_compiler.rb +85 -0
- data/lib/rails/metro/tui/app.rb +119 -0
- data/lib/rails/metro/tui/steps/app_name_step.rb +57 -0
- data/lib/rails/metro/tui/steps/database_step.rb +53 -0
- data/lib/rails/metro/tui/steps/pack_step.rb +248 -0
- data/lib/rails/metro/tui/steps/review_step.rb +94 -0
- data/lib/rails/metro/tui/styles.rb +47 -0
- data/lib/rails/metro/tui.rb +37 -0
- data/lib/rails/metro/version.rb +5 -0
- data/lib/rails/metro.rb +9 -0
- metadata +206 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
class FeatureRegistry
|
|
4
|
+
class DependencyError < StandardError; end
|
|
5
|
+
class ConflictError < StandardError; end
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@packs = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def register(pack_class)
|
|
12
|
+
name = pack_class.pack_name
|
|
13
|
+
raise ArgumentError, "Pack must define a pack_name" unless name
|
|
14
|
+
@packs[name] = pack_class
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def all
|
|
18
|
+
@packs.values
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def names
|
|
22
|
+
@packs.keys.sort
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def find(name)
|
|
26
|
+
@packs[name.to_s]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def categories
|
|
30
|
+
all.group_by { |p| p.category }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def resolve(selected_names)
|
|
34
|
+
selected = selected_names.map(&:to_s)
|
|
35
|
+
validate_existence!(selected)
|
|
36
|
+
validate_conflicts!(selected)
|
|
37
|
+
expand_dependencies(selected)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def as_catalog
|
|
41
|
+
all.sort_by(&:pack_name).map do |pack_class|
|
|
42
|
+
{
|
|
43
|
+
name: pack_class.pack_name,
|
|
44
|
+
description: pack_class.description,
|
|
45
|
+
category: pack_class.category,
|
|
46
|
+
depends_on: pack_class.depends_on,
|
|
47
|
+
conflicts_with: pack_class.conflicts_with
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def validate_selection(selected_names)
|
|
53
|
+
selected = selected_names.map(&:to_s)
|
|
54
|
+
result = {valid: true, errors: [], auto_added: []}
|
|
55
|
+
|
|
56
|
+
unknown = selected - @packs.keys
|
|
57
|
+
if unknown.any?
|
|
58
|
+
result[:valid] = false
|
|
59
|
+
result[:errors] << "Unknown packs: #{unknown.join(", ")}"
|
|
60
|
+
return result
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
selected.each do |name|
|
|
64
|
+
pack = @packs[name]
|
|
65
|
+
conflicting = pack.conflicts_with & selected
|
|
66
|
+
if conflicting.any?
|
|
67
|
+
result[:valid] = false
|
|
68
|
+
result[:errors] << "Pack '#{name}' conflicts with: #{conflicting.join(", ")}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
return result unless result[:valid]
|
|
73
|
+
|
|
74
|
+
resolved = expand_dependencies(selected)
|
|
75
|
+
resolved_names = resolved.map(&:pack_name)
|
|
76
|
+
result[:auto_added] = resolved_names - selected
|
|
77
|
+
result
|
|
78
|
+
rescue DependencyError => e
|
|
79
|
+
result[:valid] = false
|
|
80
|
+
result[:errors] << e.message
|
|
81
|
+
result
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.default
|
|
85
|
+
@default ||= build_default
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.build_default
|
|
89
|
+
registry = new
|
|
90
|
+
load_builtin_packs
|
|
91
|
+
builtin_pack_classes.each { |klass| registry.register(klass) }
|
|
92
|
+
registry
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.load_builtin_packs
|
|
96
|
+
Dir.glob(File.join(__dir__, "packs", "**", "*_pack.rb")).sort.each { |f| require f }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.builtin_pack_classes
|
|
100
|
+
ObjectSpace.each_object(Class).select { |c|
|
|
101
|
+
c < FeaturePack && c.pack_name && c.name&.start_with?("Rails::Metro::Packs::")
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def validate_existence!(names)
|
|
108
|
+
unknown = names - @packs.keys
|
|
109
|
+
if unknown.any?
|
|
110
|
+
raise DependencyError, "Unknown packs: #{unknown.join(", ")}. Available: #{@packs.keys.sort.join(", ")}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def validate_conflicts!(names)
|
|
115
|
+
names.each do |name|
|
|
116
|
+
pack = @packs[name]
|
|
117
|
+
conflicting = pack.conflicts_with & names
|
|
118
|
+
if conflicting.any?
|
|
119
|
+
raise ConflictError, "Pack '#{name}' conflicts with: #{conflicting.join(", ")}"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def expand_dependencies(names)
|
|
125
|
+
resolved = []
|
|
126
|
+
visited = Set.new
|
|
127
|
+
names.each { |name| visit(name, resolved, visited, []) }
|
|
128
|
+
resolved
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def visit(name, resolved, visited, stack)
|
|
132
|
+
return if visited.include?(name)
|
|
133
|
+
if stack.include?(name)
|
|
134
|
+
raise DependencyError, "Circular dependency: #{(stack + [name]).join(" -> ")}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
stack.push(name)
|
|
138
|
+
pack = @packs[name]
|
|
139
|
+
pack.depends_on.each { |dep| visit(dep, resolved, visited, stack) }
|
|
140
|
+
stack.pop
|
|
141
|
+
|
|
142
|
+
visited.add(name)
|
|
143
|
+
resolved << @packs[name]
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class AasmPack < FeaturePack
|
|
5
|
+
pack_name "aasm"
|
|
6
|
+
description "AASM for state machines in ActiveRecord models"
|
|
7
|
+
category "data"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "aasm"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def post_install_notes
|
|
20
|
+
[
|
|
21
|
+
"AASM: Add `include AASM` to models",
|
|
22
|
+
"AASM: Define states with `aasm do; state :draft, initial: true; state :published; end`",
|
|
23
|
+
"AASM: Define events with `event :publish do; transitions from: :draft, to: :published; end`"
|
|
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 AbTestingPack < FeaturePack
|
|
5
|
+
pack_name "ab_testing"
|
|
6
|
+
description "Split for A/B testing and experiments"
|
|
7
|
+
category "data"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "split", version: "~> 4.0"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'create_file "config/initializers/split.rb", <<~RUBY',
|
|
18
|
+
" Split.configure do |config|",
|
|
19
|
+
" config.allow_multiple_experiments = true",
|
|
20
|
+
" config.persistence = Split::Persistence::SessionAdapter",
|
|
21
|
+
" end",
|
|
22
|
+
"RUBY",
|
|
23
|
+
"",
|
|
24
|
+
'route "mount Split::Dashboard, at: \\"/split\\""'
|
|
25
|
+
]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def post_install_notes
|
|
29
|
+
[
|
|
30
|
+
"Split: Requires Redis for experiment storage",
|
|
31
|
+
"Split: Use `ab_test(\"button_color\", \"red\", \"blue\")` in views/controllers",
|
|
32
|
+
"Split: Call `ab_finished(\"button_color\")` on conversion",
|
|
33
|
+
"Split: Dashboard at /split"
|
|
34
|
+
]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class ActionTextPack < FeaturePack
|
|
5
|
+
pack_name "action_text"
|
|
6
|
+
description "Action Text for rich text editing with Trix"
|
|
7
|
+
category "ui"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def template_lines
|
|
14
|
+
[
|
|
15
|
+
'rails_command "action_text:install"'
|
|
16
|
+
]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def post_install_notes
|
|
20
|
+
[
|
|
21
|
+
"Action Text: Run `bin/rails db:migrate` to create rich text tables",
|
|
22
|
+
"Action Text: Add `has_rich_text :body` to models",
|
|
23
|
+
"Action Text: Use `<%= form.rich_text_area :body %>` in forms"
|
|
24
|
+
]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class ActiveadminPack < FeaturePack
|
|
5
|
+
pack_name "activeadmin"
|
|
6
|
+
description "ActiveAdmin -- classic, battle-tested admin framework"
|
|
7
|
+
category "admin"
|
|
8
|
+
conflicts_with "avo", "administrate", "madmin"
|
|
9
|
+
|
|
10
|
+
def gems
|
|
11
|
+
[
|
|
12
|
+
{name: "activeadmin"}
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def template_lines
|
|
17
|
+
[
|
|
18
|
+
'rails_command "generate active_admin:install --skip-users"'
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def post_install_notes
|
|
23
|
+
[
|
|
24
|
+
"ActiveAdmin: Run `bin/rails db:migrate` to create admin tables",
|
|
25
|
+
"ActiveAdmin: Access admin at /admin",
|
|
26
|
+
"ActiveAdmin: Register resources with `bin/rails g active_admin:resource User`"
|
|
27
|
+
]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class ActivityFeedPack < FeaturePack
|
|
5
|
+
pack_name "activity_feed"
|
|
6
|
+
description "Public Activity for activity feeds and timelines"
|
|
7
|
+
category "data"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "public_activity"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'rails_command "generate public_activity:migration"'
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def post_install_notes
|
|
22
|
+
[
|
|
23
|
+
"Activity Feed: Run `bin/rails db:migrate` to create activities table",
|
|
24
|
+
"Activity Feed: Add `include PublicActivity::Model` and `tracked` to models",
|
|
25
|
+
"Activity Feed: Query with `PublicActivity::Activity.order(created_at: :desc)`"
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class ActsAsListPack < FeaturePack
|
|
5
|
+
pack_name "acts_as_list"
|
|
6
|
+
description "Acts As List for sortable, orderable records"
|
|
7
|
+
category "data"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "acts_as_list"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def post_install_notes
|
|
20
|
+
[
|
|
21
|
+
"Acts As List: Add `acts_as_list` to models with a `position` integer column",
|
|
22
|
+
"Acts As List: Use `item.move_to_top`, `item.move_lower`, `item.insert_at(2)`"
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class ActsAsVotablePack < FeaturePack
|
|
5
|
+
pack_name "acts_as_votable"
|
|
6
|
+
description "Acts As Votable for likes, upvotes, and reactions"
|
|
7
|
+
category "data"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "acts_as_votable"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'rails_command "generate acts_as_votable:migration"'
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def post_install_notes
|
|
22
|
+
[
|
|
23
|
+
"Votable: Run `bin/rails db:migrate` to create votes table",
|
|
24
|
+
"Votable: Add `acts_as_votable` to models that can be voted on",
|
|
25
|
+
"Votable: Add `acts_as_voter` to User model",
|
|
26
|
+
"Votable: Use `user.likes @post` and `@post.liked_by user`"
|
|
27
|
+
]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class AdministratePack < FeaturePack
|
|
5
|
+
pack_name "administrate"
|
|
6
|
+
description "Administrate -- Thoughtbot's lightweight admin framework"
|
|
7
|
+
category "admin"
|
|
8
|
+
conflicts_with "avo", "activeadmin", "madmin"
|
|
9
|
+
|
|
10
|
+
def gems
|
|
11
|
+
[
|
|
12
|
+
{name: "administrate"}
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def template_lines
|
|
17
|
+
[
|
|
18
|
+
'rails_command "generate administrate:install"'
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def post_install_notes
|
|
23
|
+
[
|
|
24
|
+
"Administrate: Access admin at /admin",
|
|
25
|
+
"Administrate: Generate dashboards with `bin/rails g administrate:dashboard User`"
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class AfterPartyPack < FeaturePack
|
|
5
|
+
pack_name "after_party"
|
|
6
|
+
description "AfterParty for one-time post-deploy tasks"
|
|
7
|
+
category "ops"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "after_party"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'rails_command "generate after_party:install"'
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def post_install_notes
|
|
22
|
+
[
|
|
23
|
+
"AfterParty: Generate tasks with `bin/rails g after_party:task seed_admin_user`",
|
|
24
|
+
"AfterParty: Run with `bin/rails after_party:run`",
|
|
25
|
+
"AfterParty: Tasks only run once (tracked in task_records table)"
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class AhoyPack < FeaturePack
|
|
5
|
+
pack_name "ahoy"
|
|
6
|
+
description "Ahoy for visit and event tracking (first-party analytics)"
|
|
7
|
+
category "analytics"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "ahoy_matey"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'rails_command "generate ahoy:install"'
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def post_install_notes
|
|
22
|
+
[
|
|
23
|
+
"Ahoy: Run `bin/rails db:migrate` to create visits and events tables",
|
|
24
|
+
"Ahoy: Track events with `ahoy.track \"Viewed product\", product_id: product.id`",
|
|
25
|
+
"Ahoy: Visits auto-tracked -- access with `current_visit` in controllers"
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class AmplitudePack < FeaturePack
|
|
5
|
+
pack_name "amplitude"
|
|
6
|
+
description "Amplitude for product analytics, funnels, and retention"
|
|
7
|
+
category "analytics"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "amplitude-api"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'create_file "config/initializers/amplitude.rb", <<~RUBY',
|
|
18
|
+
" AmplitudeAPI.config.api_key = Rails.application.credentials.dig(:amplitude, :api_key) || ENV[\"AMPLITUDE_API_KEY\"]",
|
|
19
|
+
"RUBY",
|
|
20
|
+
"",
|
|
21
|
+
'create_file "app/views/layouts/_amplitude.html.erb", <<~ERB',
|
|
22
|
+
" <script type=\"text/javascript\">",
|
|
23
|
+
' !function(){"use strict";!function(e,t){var r=e.amplitude||{_q:[],_iq:{}};if(r.invoked)e.console&&console.error&&console.error("Amplitude snippet has been loaded.");else{var n=function(e,t){e.prototype[t]=function(){return this._q.push({name:t,args:Array.prototype.slice.call(arguments,0)}),this}},s=function(e,t,r){return function(n){e._q.push({name:t,args:Array.prototype.slice.call(arguments,0),resolve:n})}},o=function(e,t,r){e._q.push({name:t,args:Array.prototype.slice.call(arguments,0)})},i=function(e,t,r){e[t]=function(){if(r)return{promise:new Promise(s(e,t,r))};o(e,t,r)}},a=function(e){for(var t=0;t<g.length;t++)i(e,g[t],!1);for(var r=0;r<m.length;r++)i(e,m[r],!0)};r.invoked=!0;var c=t.createElement("script");c.type="text/javascript",c.integrity="sha384-PPfHw98myKtJkA9OdPBMQ6n8yvUaYk0EyUQccFSIQGmB05K6aAMZwvv8d/EmYKhv",c.crossOrigin="anonymous",c.async=!0,c.src="https://cdn.amplitude.com/libs/analytics-browser-2.11.1-min.js.gz",c.onload=function(){e.amplitude.runQueuedFunctions||console.log("[Amplitude] Error: could not load SDK")};var u=t.getElementsByTagName("script")[0];u.parentNode.insertBefore(c,u);for(var l=function(){return this._q=[],this},p=["add","append","clearAll","prepend","set","setOnce","unset","preInsert","postInsert","remove","getUserProperties"],d=0;d<p.length;d++)n(l,p[d]);r.Identify=l;for(var f=function(){return this._q=[],this},v=["getEventProperties","setProductId","setQuantity","setPrice","setRevenue","setRevenueType","setEventProperties"],y=0;y<v.length;y++)n(f,v[y]);r.Revenue=f;var g=["getDeviceId","setDeviceId","getSessionId","setSessionId","getUserId","setUserId","setOptOut","setTransport","reset","extendSession"],m=["init","add","remove","track","logEvent","identify","groupIdentify","setGroup","revenue","flush"];a(r),r.createInstance=function(e){return r._iq[e]={_q:[]},a(r._iq[e]),r._iq[e]},e.amplitude=r}}(window,document)}();',
|
|
24
|
+
' amplitude.init("<%%= Rails.application.credentials.dig(:amplitude, :api_key) || ENV["AMPLITUDE_API_KEY"] %>");',
|
|
25
|
+
" </script>",
|
|
26
|
+
"ERB",
|
|
27
|
+
"",
|
|
28
|
+
'inject_into_file "app/views/layouts/application.html.erb", after: "<head>\\n" do',
|
|
29
|
+
' " <%= render \\"layouts/amplitude\\" %>\\n"',
|
|
30
|
+
"end"
|
|
31
|
+
]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def post_install_notes
|
|
35
|
+
[
|
|
36
|
+
"Amplitude: Add your API key with `bin/rails credentials:edit`:",
|
|
37
|
+
" amplitude:",
|
|
38
|
+
" api_key: your_api_key",
|
|
39
|
+
"Amplitude: Use `AmplitudeAPI.track(event)` server-side for backend events"
|
|
40
|
+
]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class AnnotatePack < FeaturePack
|
|
5
|
+
pack_name "annotate"
|
|
6
|
+
description "Annotate models and routes with schema info"
|
|
7
|
+
category "data"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "annotaterb", group: :development}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'rails_command "generate annotate_rb:install"'
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def post_install_notes
|
|
22
|
+
[
|
|
23
|
+
"Annotate: Schema comments are auto-added to models after migrations",
|
|
24
|
+
"Annotate: Run `bin/rails annotate_models` to annotate manually"
|
|
25
|
+
]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class ApiCorsPack < FeaturePack
|
|
5
|
+
pack_name "api_cors"
|
|
6
|
+
description "Rack CORS for cross-origin API requests"
|
|
7
|
+
category "api"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "rack-cors"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'create_file "config/initializers/cors.rb", <<~RUBY',
|
|
18
|
+
" Rails.application.config.middleware.insert_before 0, Rack::Cors do",
|
|
19
|
+
" allow do",
|
|
20
|
+
' origins Rails.application.credentials.dig(:cors, :origins) || ENV.fetch("CORS_ORIGINS", "http://localhost:3000")',
|
|
21
|
+
"",
|
|
22
|
+
" resource \"*\",",
|
|
23
|
+
" headers: :any,",
|
|
24
|
+
" methods: %i[get post put patch delete options head],",
|
|
25
|
+
" credentials: true",
|
|
26
|
+
" end",
|
|
27
|
+
" end",
|
|
28
|
+
"RUBY"
|
|
29
|
+
]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def post_install_notes
|
|
33
|
+
[
|
|
34
|
+
"CORS: Update allowed origins in config/initializers/cors.rb",
|
|
35
|
+
"CORS: Set CORS_ORIGINS env var in production (comma-separated domains)"
|
|
36
|
+
]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
module Metro
|
|
3
|
+
module Packs
|
|
4
|
+
class ApiDocsPack < FeaturePack
|
|
5
|
+
pack_name "api_docs"
|
|
6
|
+
description "Rswag for Swagger/OpenAPI documentation from tests"
|
|
7
|
+
category "api"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "rswag-api"},
|
|
12
|
+
{name: "rswag-ui"},
|
|
13
|
+
{name: "rswag-specs", group: :test}
|
|
14
|
+
]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def template_lines
|
|
18
|
+
[
|
|
19
|
+
'rails_command "generate rswag:api:install"',
|
|
20
|
+
'rails_command "generate rswag:ui:install"'
|
|
21
|
+
]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def post_install_notes
|
|
25
|
+
[
|
|
26
|
+
"Rswag: Write request specs with `describe 'API', swagger_doc: 'v1/swagger.yaml'`",
|
|
27
|
+
"Rswag: Generate docs with `rake rswag:specs:swaggerize`",
|
|
28
|
+
"Rswag: Swagger UI available at /api-docs"
|
|
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 ApiGuardPack < FeaturePack
|
|
5
|
+
pack_name "api_guard"
|
|
6
|
+
description "API Guard for JWT authentication in Rails APIs"
|
|
7
|
+
category "api"
|
|
8
|
+
|
|
9
|
+
def gems
|
|
10
|
+
[
|
|
11
|
+
{name: "api_guard"}
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_lines
|
|
16
|
+
[
|
|
17
|
+
'rails_command "generate api_guard:initializer"'
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def post_install_notes
|
|
22
|
+
[
|
|
23
|
+
"API Guard: Run `bin/rails db:migrate` to create token tables",
|
|
24
|
+
"API Guard: Add `api_guard_routes for: 'users'` to routes",
|
|
25
|
+
"API Guard: Endpoints: POST /users/sign_in, DELETE /users/sign_out, POST /users/tokens"
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|