railsforge 1.0.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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +528 -0
  4. data/bin/railsforge +8 -0
  5. data/lib/railsforge/analyzers/base_analyzer.rb +41 -0
  6. data/lib/railsforge/analyzers/controller_analyzer.rb +83 -0
  7. data/lib/railsforge/analyzers/database_analyzer.rb +55 -0
  8. data/lib/railsforge/analyzers/metrics_analyzer.rb +55 -0
  9. data/lib/railsforge/analyzers/model_analyzer.rb +74 -0
  10. data/lib/railsforge/analyzers/performance_analyzer.rb +161 -0
  11. data/lib/railsforge/analyzers/refactor_analyzer.rb +118 -0
  12. data/lib/railsforge/analyzers/security_analyzer.rb +169 -0
  13. data/lib/railsforge/analyzers/spec_analyzer.rb +58 -0
  14. data/lib/railsforge/api_generator.rb +397 -0
  15. data/lib/railsforge/audit.rb +289 -0
  16. data/lib/railsforge/cli.rb +671 -0
  17. data/lib/railsforge/config.rb +181 -0
  18. data/lib/railsforge/database_analyzer.rb +300 -0
  19. data/lib/railsforge/doctor.rb +250 -0
  20. data/lib/railsforge/feature_generator.rb +560 -0
  21. data/lib/railsforge/generator.rb +313 -0
  22. data/lib/railsforge/generators/base_generator.rb +70 -0
  23. data/lib/railsforge/generators/demo_generator.rb +307 -0
  24. data/lib/railsforge/generators/devops_generator.rb +287 -0
  25. data/lib/railsforge/generators/monitoring_generator.rb +134 -0
  26. data/lib/railsforge/generators/service_generator.rb +122 -0
  27. data/lib/railsforge/generators/stimulus_controller_generator.rb +129 -0
  28. data/lib/railsforge/generators/test_generator.rb +289 -0
  29. data/lib/railsforge/generators/view_component_generator.rb +169 -0
  30. data/lib/railsforge/graph.rb +270 -0
  31. data/lib/railsforge/loader.rb +56 -0
  32. data/lib/railsforge/mailer_generator.rb +191 -0
  33. data/lib/railsforge/plugins/plugin_loader.rb +60 -0
  34. data/lib/railsforge/plugins.rb +30 -0
  35. data/lib/railsforge/profiles/admin_app.yml +49 -0
  36. data/lib/railsforge/profiles/api_only.yml +47 -0
  37. data/lib/railsforge/profiles/blog.yml +47 -0
  38. data/lib/railsforge/profiles/standard.yml +44 -0
  39. data/lib/railsforge/profiles.rb +99 -0
  40. data/lib/railsforge/refactor_analyzer.rb +401 -0
  41. data/lib/railsforge/refactor_controller.rb +277 -0
  42. data/lib/railsforge/refactors/refactor_controller.rb +117 -0
  43. data/lib/railsforge/template_loader.rb +105 -0
  44. data/lib/railsforge/templates/v1/form/spec_template.rb +18 -0
  45. data/lib/railsforge/templates/v1/form/template.rb +28 -0
  46. data/lib/railsforge/templates/v1/job/spec_template.rb +17 -0
  47. data/lib/railsforge/templates/v1/job/template.rb +13 -0
  48. data/lib/railsforge/templates/v1/policy/spec_template.rb +41 -0
  49. data/lib/railsforge/templates/v1/policy/template.rb +57 -0
  50. data/lib/railsforge/templates/v1/presenter/spec_template.rb +12 -0
  51. data/lib/railsforge/templates/v1/presenter/template.rb +13 -0
  52. data/lib/railsforge/templates/v1/query/spec_template.rb +12 -0
  53. data/lib/railsforge/templates/v1/query/template.rb +16 -0
  54. data/lib/railsforge/templates/v1/serializer/spec_template.rb +13 -0
  55. data/lib/railsforge/templates/v1/serializer/template.rb +11 -0
  56. data/lib/railsforge/templates/v1/service/spec_template.rb +12 -0
  57. data/lib/railsforge/templates/v1/service/template.rb +25 -0
  58. data/lib/railsforge/templates/v1/stimulus_controller/template.rb +35 -0
  59. data/lib/railsforge/templates/v1/view_component/template.rb +24 -0
  60. data/lib/railsforge/templates/v2/job/template.rb +49 -0
  61. data/lib/railsforge/templates/v2/query/template.rb +66 -0
  62. data/lib/railsforge/templates/v2/service/spec_template.rb +33 -0
  63. data/lib/railsforge/templates/v2/service/template.rb +71 -0
  64. data/lib/railsforge/templates/v3/job/template.rb +72 -0
  65. data/lib/railsforge/templates/v3/query/spec_template.rb +54 -0
  66. data/lib/railsforge/templates/v3/query/template.rb +115 -0
  67. data/lib/railsforge/templates/v3/service/spec_template.rb +51 -0
  68. data/lib/railsforge/templates/v3/service/template.rb +84 -0
  69. data/lib/railsforge/version.rb +5 -0
  70. data/lib/railsforge/wizard.rb +265 -0
  71. data/lib/railsforge/wizard_tui.rb +286 -0
  72. data/lib/railsforge.rb +13 -0
  73. metadata +216 -0
@@ -0,0 +1,270 @@
1
+ # Graph module for RailsForge
2
+ # Generates dependency graphs using Graphviz
3
+
4
+ require 'fileutils'
5
+
6
+ module RailsForge
7
+ # Graph class generates visual dependency graphs
8
+ class Graph
9
+ # Initialize the graph generator
10
+ def initialize(base_path = nil)
11
+ @base_path = base_path || find_rails_app_path
12
+ raise GraphError, "Not in a Rails application directory" unless @base_path
13
+
14
+ @output_dir = File.join(@base_path, "tmp", "railsforge")
15
+ @relationships = []
16
+ end
17
+
18
+ # Generate dependency graph
19
+ # @return [String] Path to generated files
20
+ def generate
21
+ puts "Analyzing Rails app structure..."
22
+ puts ""
23
+
24
+ # Collect relationships
25
+ analyze_controllers
26
+ analyze_services
27
+ analyze_queries
28
+ analyze_models
29
+ analyze_jobs
30
+
31
+ # Create output directory
32
+ FileUtils.mkdir_p(@output_dir)
33
+
34
+ # Generate DOT file
35
+ dot_file = generate_dot_file
36
+
37
+ # Generate SVG if Graphviz is available
38
+ svg_file = generate_svg(dot_file)
39
+
40
+ puts "Dependency graph generated!"
41
+ puts " DOT: #{dot_file}"
42
+ puts " SVG: #{svg_file}" if svg_file
43
+
44
+ { dot: dot_file, svg: svg_file }
45
+ end
46
+
47
+ private
48
+
49
+ # Find Rails app path
50
+ def find_rails_app_path
51
+ path = Dir.pwd
52
+ max_depth = 10
53
+
54
+ max_depth.times do
55
+ return path if File.exist?(File.join(path, "config", "application.rb"))
56
+ parent = File.dirname(path)
57
+ break if parent == path
58
+ path = parent
59
+ end
60
+
61
+ nil
62
+ end
63
+
64
+ # Analyze controllers for dependencies
65
+ def analyze_controllers
66
+ controllers_dir = File.join(@base_path, "app", "controllers")
67
+ return unless Dir.exist?(controllers_dir)
68
+
69
+ Dir.glob(File.join(controllers_dir, "**", "*_controller.rb")).each do |file|
70
+ next if file.end_with?("_application_controller.rb")
71
+
72
+ controller_name = File.basename(file, "_controller.rb").camelize
73
+ content = File.read(file)
74
+
75
+ # Find service dependencies
76
+ content.scan(/(\w+Service)\.call/).each do |service|
77
+ @relationships << { from: controller_name, to: service[0], type: "uses" }
78
+ end
79
+
80
+ # Find query dependencies
81
+ content.scan(/(\w+Query)\.new|(\w+)\.find_/).each do |match|
82
+ query = match[0] || match[1]
83
+ @relationships << { from: controller_name, to: query, type: "queries" }
84
+ end
85
+
86
+ # Find model dependencies
87
+ content.scan(/@(\w+)\s*=/).each do |model|
88
+ @relationships << { from: controller_name, to: model[0].singularize.camelize, type: "uses" }
89
+ end
90
+
91
+ # Find job dependencies
92
+ content.scan(/(\w+Job)\.perform_later/).each do |job|
93
+ @relationships << { from: controller_name, to: job[0], type: "enqueues" }
94
+ end
95
+ end
96
+ end
97
+
98
+ # Analyze services for dependencies
99
+ def analyze_services
100
+ services_dir = File.join(@base_path, "app", "services")
101
+ return unless Dir.exist?(services_dir)
102
+
103
+ Dir.glob(File.join(services_dir, "**", "*.rb")).each do |file|
104
+ service_name = File.basename(file, ".rb").camelize
105
+ content = File.read(file)
106
+
107
+ # Find model dependencies
108
+ content.scan(/(\w+)\.find|(\w+)\.where/).each do |match|
109
+ model = match[0] || match[1]
110
+ @relationships << { from: service_name, to: model.singularize.camelize, type: "queries" }
111
+ end
112
+
113
+ # Find other service dependencies
114
+ content.scan(/(\w+Service)\.call/).each do |service|
115
+ @relationships << { from: service_name, to: service[0], type: "uses" }
116
+ end
117
+
118
+ # Find job dependencies
119
+ content.scan(/(\w+Job)\.perform/).each do |job|
120
+ @relationships << { from: service_name, to: job[0], type: "enqueues" }
121
+ end
122
+ end
123
+ end
124
+
125
+ # Analyze queries for dependencies
126
+ def analyze_queries
127
+ queries_dir = File.join(@base_path, "app", "queries")
128
+ return unless Dir.exist?(queries_dir)
129
+
130
+ Dir.glob(File.join(queries_dir, "**", "*.rb")).each do |file|
131
+ query_name = File.basename(file, ".rb").camelize
132
+ content = File.read(file)
133
+
134
+ # Find model dependencies
135
+ content.scan(/(\w+)\.find|(\w+)\.where/).each do |match|
136
+ model = match[0] || match[1]
137
+ @relationships << { from: query_name, to: model.singularize.camelize, type: "queries" }
138
+ end
139
+ end
140
+ end
141
+
142
+ # Analyze models for associations
143
+ def analyze_models
144
+ models_dir = File.join(@base_path, "app", "models")
145
+ return unless Dir.exist?(models_dir)
146
+
147
+ Dir.glob(File.join(models_dir, "**", "*.rb")).each do |file|
148
+ next if file.end_with?("_application_name = File.basename(file, ".rb.rb")
149
+
150
+ model").camelize
151
+ content = File.read(file)
152
+
153
+ # Find associations
154
+ content.scan(/belongs_to\s+:(\w+)/).each do |assoc|
155
+ @relationships << { from: model_name, to: assoc[0].camelize, type: "belongs_to" }
156
+ end
157
+
158
+ content.scan(/has_many\s+:(\w+)/).each do |assoc|
159
+ @relationships << { from: model_name, to: assoc[0].camelize, type: "has_many" }
160
+ end
161
+
162
+ content.scan(/has_one\s+:(\w+)/).each do |assoc|
163
+ @relationships << { from: model_name, to: assoc[0].camelize, type: "has_one" }
164
+ end
165
+ end
166
+ end
167
+
168
+ # Analyze jobs for dependencies
169
+ def analyze_jobs
170
+ jobs_dir = File.join(@base_path, "app", "jobs")
171
+ return unless Dir.exist?(jobs_dir)
172
+
173
+ Dir.glob(File.join(jobs_dir, "**", "*.rb")).each do |file|
174
+ job_name = File.basename(file, ".rb").camelize
175
+ content = File.read(file)
176
+
177
+ # Find service dependencies
178
+ content.scan(/(\w+Service)\.call/).each do |service|
179
+ @relationships << { from: job_name, to: service[0], type: "uses" }
180
+ end
181
+ end
182
+ end
183
+
184
+ # Generate DOT file
185
+ def generate_dot_file
186
+ dot_path = File.join(@output_dir, "dependency_graph.dot")
187
+
188
+ # Define colors for different types
189
+ type_colors = {
190
+ "controller" => "#3498db",
191
+ "service" => "#2ecc71",
192
+ "query" => "#9b59b6",
193
+ "model" => "#e74c3c",
194
+ "job" => "#f39c12",
195
+ "uses" => "#95a5a6",
196
+ "queries" => "#95a5a6",
197
+ "enqueues" => "#95a5a6",
198
+ "belongs_to" => "#e67e22",
199
+ "has_many" => "#1abc9c",
200
+ "has_one" => "#16a085"
201
+ }
202
+
203
+ dot_content = []
204
+ dot_content << "digraph RailsApp {"
205
+ dot_content << " rankdir=LR;"
206
+ dot_content << " node [shape=box, style=rounded];"
207
+ dot_content << ""
208
+
209
+ # Collect all nodes
210
+ nodes = Set.new
211
+ @relationships.each do |rel|
212
+ nodes.add(rel[:from])
213
+ nodes.add(rel[:to])
214
+ end
215
+
216
+ # Define node styles
217
+ nodes.each do |node|
218
+ type = detect_node_type(node)
219
+ color = type_colors[type] || "#95a5a6"
220
+ dot_content << " \"#{node}\" [fillcolor=\"#{color}\", style=filled, fontcolor=white];"
221
+ end
222
+
223
+ dot_content << ""
224
+
225
+ # Define edges
226
+ @relationships.each do |rel|
227
+ style = "solid"
228
+ color = type_colors[rel[:type]] || "#95a5a6"
229
+ arrow = "normal"
230
+
231
+ if %w[belongs_to has_many has_one].include?(rel[:type])
232
+ style = "dashed"
233
+ end
234
+
235
+ dot_content << " \"#{rel[:from]}\" -> \"#{rel[:to]}\" [style=#{style}, color=#{color}, arrowhead=#{arrow}];"
236
+ end
237
+
238
+ dot_content << "}"
239
+
240
+ File.write(dot_path, dot_content.join("\n"))
241
+ dot_path
242
+ end
243
+
244
+ # Detect node type based on name
245
+ def detect_node_type(name)
246
+ return "controller" if name.downcase.end_with?("controller")
247
+ return "service" if name.downcase.end_with?("service")
248
+ return "query" if name.downcase.start_with?("find") || name.downcase.end_with?("query")
249
+ return "model" if name.downcase.end_with?("job")
250
+ return "job" if name.downcase.end_with?("job")
251
+ "model"
252
+ end
253
+
254
+ # Generate SVG from DOT file
255
+ def generate_svg(dot_path)
256
+ svg_path = dot_path.sub(".dot", ".svg")
257
+
258
+ # Check if Graphviz is installed
259
+ return nil unless system("which dot > /dev/null 2>&1")
260
+
261
+ system("dot -Tsvg #{dot_path} -o #{svg_path} 2>/dev/null")
262
+ svg_path if File.exist?(svg_path)
263
+ rescue
264
+ nil
265
+ end
266
+
267
+ # Error class
268
+ class GraphError < StandardError; end
269
+ end
270
+ end
@@ -0,0 +1,56 @@
1
+ # RailsForge modular loader
2
+ # This file centralizes all require statements
3
+
4
+ require "fileutils"
5
+
6
+ # Get the lib directory path
7
+ LIB_DIR = File.dirname(__FILE__)
8
+
9
+ # Core modules
10
+ require_relative "version"
11
+ require_relative "profiles"
12
+ require_relative "template_loader"
13
+
14
+ # CLI
15
+ require_relative "cli"
16
+
17
+ # Generators (modular)
18
+ require_relative "generators/base_generator"
19
+ require_relative "generators/service_generator"
20
+ require_relative "generators/view_component_generator"
21
+ require_relative "generators/stimulus_controller_generator"
22
+ require_relative "generators/demo_generator"
23
+ require_relative "generators/devops_generator"
24
+ require_relative "generators/monitoring_generator"
25
+ require_relative "generators/test_generator"
26
+
27
+ # Analyzers (modular)
28
+ require_relative "analyzers/base_analyzer"
29
+ require_relative "analyzers/controller_analyzer"
30
+ require_relative "analyzers/model_analyzer"
31
+ require_relative "analyzers/spec_analyzer"
32
+ require_relative "analyzers/database_analyzer"
33
+ require_relative "analyzers/metrics_analyzer"
34
+ require_relative "analyzers/refactor_analyzer"
35
+ require_relative "analyzers/security_analyzer"
36
+ require_relative "analyzers/performance_analyzer"
37
+
38
+ # Refactors
39
+ require_relative "refactors/refactor_controller"
40
+
41
+ # Plugins
42
+ require_relative "plugins"
43
+
44
+ # Additional features (legacy compatibility)
45
+ require_relative "mailer_generator"
46
+ require_relative "feature_generator"
47
+ require_relative "api_generator"
48
+ require_relative "audit"
49
+ require_relative "doctor"
50
+ require_relative "graph"
51
+ require_relative "wizard"
52
+ require_relative "wizard_tui"
53
+ require_relative "config"
54
+
55
+ # Generator module (app creation)
56
+ require_relative "generator"
@@ -0,0 +1,191 @@
1
+ module RailsForge
2
+ # MailerGenerator module handles generating mailers
3
+ module MailerGenerator
4
+ # Error class for invalid mailer names
5
+ class InvalidMailerNameError < StandardError; end
6
+
7
+ # Validates the mailer name to ensure it's valid Ruby class name
8
+ def self.validate_mailer_name(name)
9
+ if name.nil? || name.strip.empty?
10
+ raise InvalidMailerNameError, "Mailer name cannot be empty"
11
+ end
12
+
13
+ unless name =~ /\A[A-Z][a-zA-Z0-9]*\z/
14
+ raise InvalidMailerNameError, "Mailer name must be in PascalCase (e.g., UserMailer)"
15
+ end
16
+ end
17
+
18
+ # Generates a mailer file with views and optionally an RSpec test
19
+ def self.generate(mailer_name, with_spec: true, with_jobs: false)
20
+ validate_mailer_name(mailer_name)
21
+
22
+ base_path = find_rails_app_path
23
+ unless base_path
24
+ raise "Not in a Rails application directory"
25
+ end
26
+
27
+ generate_mailer_file(base_path, mailer_name, with_jobs)
28
+ generate_view_files(base_path, mailer_name)
29
+
30
+ if with_spec
31
+ generate_spec_file(base_path, mailer_name)
32
+ end
33
+
34
+ "Mailer '#{mailer_name}' generated successfully!"
35
+ end
36
+
37
+ def self.generate_mailer_file(base_path, mailer_name, with_jobs = false)
38
+ mailer_dir = File.join(base_path, "app", "mailers")
39
+ FileUtils.mkdir_p(mailer_dir)
40
+
41
+ file_name = "#{mailer_name.underscore}.rb"
42
+ file_path = File.join(mailer_dir, file_name)
43
+
44
+ if File.exist?(file_path)
45
+ puts " Skipping mailer (already exists)"
46
+ return file_path
47
+ end
48
+
49
+ File.write(file_path, mailer_template(mailer_name, with_jobs))
50
+ puts " Created app/mailers/#{file_name}"
51
+ file_path
52
+ end
53
+
54
+ def self.generate_view_files(base_path, mailer_name)
55
+ views_dir = File.join(base_path, "app", "views", mailer_name.underscore)
56
+ FileUtils.mkdir_p(views_dir)
57
+
58
+ methods = ["welcome_email", "notification_email"]
59
+
60
+ methods.each do |method|
61
+ html_path = File.join(views_dir, "#{method}.html.erb")
62
+ unless File.exist?(html_path)
63
+ File.write(html_path, view_html_template(mailer_name, method))
64
+ puts " Created app/views/#{mailer_name.underscore}/#{method}.html.erb"
65
+ end
66
+
67
+ text_path = File.join(views_dir, "#{method}.text.erb")
68
+ unless File.exist?(text_path)
69
+ File.write(text_path, view_text_template(mailer_name, method))
70
+ puts " Created app/views/#{mailer_name.underscore}/#{method}.text.erb"
71
+ end
72
+ end
73
+ end
74
+
75
+ def self.generate_spec_file(base_path, mailer_name)
76
+ spec_dir = File.join(base_path, "spec", "mailers")
77
+ FileUtils.mkdir_p(spec_dir)
78
+
79
+ file_name = "#{mailer_name.underscore}_spec.rb"
80
+ file_path = File.join(spec_dir, file_name)
81
+
82
+ if File.exist?(file_path)
83
+ puts " Skipping spec (already exists)"
84
+ return file_path
85
+ end
86
+
87
+ File.write(file_path, spec_template(mailer_name))
88
+ puts " Created spec/mailers/#{file_name}"
89
+ file_path
90
+ end
91
+
92
+ def self.find_rails_app_path
93
+ path = Dir.pwd
94
+ 10.times do
95
+ return path if File.exist?(File.join(path, "config", "application.rb"))
96
+ parent = File.dirname(path)
97
+ break if parent == path
98
+ path = parent
99
+ end
100
+ nil
101
+ end
102
+
103
+ def self.mailer_template(mailer_name, with_jobs = false)
104
+ <<~RUBY
105
+ # Mailer class for #{mailer_name}
106
+ # Handles sending emails related to #{mailer_name.underscore}
107
+ #
108
+ # Usage:
109
+ # #{mailer_name}.with(user: @user).welcome_email.deliver_later
110
+ # #{mailer_name}.with(user: @user).notification_email.deliver_now
111
+ class #{mailer_name} < ApplicationMailer
112
+ # Sends a welcome email to the user
113
+ # @param user [User] The user to send the welcome email to
114
+ # @return [Mail::Message] The sent mail message
115
+ def welcome_email(user)
116
+ mail(to: user.email, subject: "Welcome to Our App!")
117
+ end
118
+
119
+ # Sends a notification email to the user
120
+ # @param user [User] The user to send the notification to
121
+ # @param message [String] The notification message
122
+ # @return [Mail::Message] The sent mail message
123
+ def notification_email(user, message: "You have a new notification")
124
+ @user = user
125
+ @message = message
126
+ mail(to: user.email, subject: "Notification from Our App")
127
+ end
128
+
129
+ # Class method to queue email via ActiveJob
130
+ # Use with --jobs flag for background delivery
131
+ # def self.deliver_with_job(user, method: :welcome_email, **args)
132
+ # #{mailer_name}Job.perform_later(user, method: method, **args)
133
+ # end
134
+ end
135
+ RUBY
136
+ end
137
+
138
+ def self.view_html_template(mailer_name, method_name)
139
+ <<~ERB
140
+ <!DOCTYPE html>
141
+ <html>
142
+ <head>
143
+ <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
144
+ <style>body { font-family: Arial, sans-serif; }</style>
145
+ </head>
146
+ <body>
147
+ <h1>#{method_name == 'welcome_email' ? 'Welcome!' : 'Notification'}</h1>
148
+ #{method_name == 'welcome_email' ? '<p>Welcome to our application!</p>' : '<p><%= @message %></p>'}
149
+ </body>
150
+ </html>
151
+ ERB
152
+ end
153
+
154
+ def self.view_text_template(mailer_name, method_name)
155
+ <<~TEXT
156
+ #{method_name == 'welcome_email' ? 'Welcome to our application!' : '<%= @message %>'}
157
+
158
+ --
159
+ Our App
160
+ TEXT
161
+ end
162
+
163
+ def self.spec_template(mailer_name)
164
+ <<~RUBY
165
+ require "rails_helper"
166
+
167
+ RSpec.describe #{mailer_name}, type: :mailer do
168
+ describe "welcome_email" do
169
+ let(:user) { User.create!(name: "Test", email: "test@example.com") }
170
+ let(:mail) { described_class.welcome_email(user) }
171
+
172
+ it "renders the headers" do
173
+ expect(mail.subject).to eq("Welcome to Our App!")
174
+ expect(mail.to).to eq([user.email])
175
+ end
176
+ end
177
+
178
+ describe "notification_email" do
179
+ let(:user) { User.create!(name: "Test", email: "test@example.com") }
180
+ let(:mail) { described_class.notification_email(user, message: "Test") }
181
+
182
+ it "renders the body" do
183
+ expect(mail.body.encoded).to include("Test")
184
+ end
185
+ end
186
+ end
187
+ RUBY
188
+ end
189
+ end
190
+
191
+ end
@@ -0,0 +1,60 @@
1
+ # Plugin loader for RailsForge
2
+ # Handles plugin discovery and loading
3
+
4
+ require 'fileutils'
5
+
6
+ module RailsForge
7
+ module Plugins
8
+ # PluginLoader manages RailsForge plugins
9
+ class PluginLoader
10
+ PLUGINS_DIR = File.expand_path('../../plugins', __FILE__)
11
+
12
+ # List available plugins
13
+ def self.list
14
+ return [] unless Dir.exist?(PLUGINS_DIR)
15
+
16
+ Dir.glob(File.join(PLUGINS_DIR, "*.rb")).map do |file|
17
+ File.basename(file, ".rb")
18
+ end
19
+ end
20
+
21
+ # Load a plugin
22
+ def self.load(plugin_name)
23
+ plugin_path = File.join(PLUGINS_DIR, "#{plugin_name}.rb")
24
+
25
+ unless File.exist?(plugin_path)
26
+ puts "Plugin '#{plugin_name}' not found"
27
+ return false
28
+ end
29
+
30
+ require plugin_path
31
+ puts "Plugin '#{plugin_name}' loaded"
32
+ true
33
+ end
34
+
35
+ # Load all plugins
36
+ def self.load_all
37
+ list.each { |plugin| load(plugin) }
38
+ end
39
+
40
+ # Create a new plugin
41
+ def self.create(plugin_name)
42
+ FileUtils.mkdir_p(PLUGINS_DIR)
43
+
44
+ path = File.join(PLUGINS_DIR, "#{plugin_name}.rb")
45
+ return if File.exist?(path)
46
+
47
+ File.write(path, <<~RUBY)
48
+ module RailsForge
49
+ module #{plugin_name.split('_').map(&:capitalize).join}
50
+ PLUGIN_NAME = "#{plugin_name}"
51
+ VERSION = "0.1.0"
52
+ end
53
+ end
54
+ RUBY
55
+
56
+ puts "Created #{path}"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,30 @@
1
+ # RailsForge Plugins module
2
+ # Provides plugin management functionality
3
+
4
+ require_relative "plugins/plugin_loader"
5
+
6
+ module RailsForge
7
+ module Plugins
8
+ # Show installed/available plugins
9
+ def self.show_installed
10
+ plugins = PluginLoader.list
11
+ if plugins.empty?
12
+ puts "No plugins installed."
13
+ puts "Create one with: railsforge plugins create <name>"
14
+ else
15
+ puts "Available plugins:"
16
+ plugins.each { |p| puts " - #{p}" }
17
+ end
18
+ end
19
+
20
+ # Load a plugin by name
21
+ def self.load(plugin_name)
22
+ PluginLoader.load(plugin_name)
23
+ end
24
+
25
+ # Create a new plugin
26
+ def self.create(plugin_name)
27
+ PluginLoader.create(plugin_name)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ # Admin App Profile Configuration
2
+ # Use with: railsforge new my_admin --profile=admin_app
3
+
4
+ name: admin_app
5
+ description: An admin dashboard application with user management, analytics, and CRUD operations
6
+
7
+ # Default folders to create
8
+ folders:
9
+ - app/controllers/admin
10
+ - app/models
11
+ - app/views/admin
12
+ - app/helpers
13
+ - app/services
14
+ - app/queries
15
+ - app/presenters
16
+ - app/policies
17
+ - app/serializers
18
+ - app/forms
19
+ - app/dashboards
20
+ - config/initializers
21
+ - config/environments
22
+ - db/migrate
23
+ - spec/models
24
+ - spec/controllers
25
+ - spec/requests
26
+ - spec/features
27
+ - spec/services
28
+ - spec/policies
29
+
30
+ # Default generators to run after app creation
31
+ generators:
32
+ - model
33
+ - scaffold
34
+ - resource
35
+
36
+ # Features to enable by default
37
+ features:
38
+ - authentication
39
+ - authorization
40
+ - admin
41
+ - pagination
42
+ - export
43
+
44
+ # Template variables
45
+ defaults:
46
+ css_framework: bootstrap
47
+ testing: rspec
48
+ database: postgresql
49
+ admin_framework: activeadmin
@@ -0,0 +1,47 @@
1
+ # API Only Profile Configuration
2
+ # Use with: railsforge new my_api --profile=api_only
3
+
4
+ name: api_only
5
+ description: A JSON API application with token authentication and versioning
6
+
7
+ # Default folders to create
8
+ folders:
9
+ - app/controllers/api
10
+ - app/controllers/api/v1
11
+ - app/models
12
+ - app/serializers
13
+ - app/services
14
+ - app/queries
15
+ - app/policies
16
+ - app/forms
17
+ - app/presenters
18
+ - config/initializers
19
+ - config/environments
20
+ - config/initializers/doorkeeper.rb
21
+ - db/migrate
22
+ - spec/models
23
+ - spec/requests
24
+ - spec/api
25
+ - spec/services
26
+ - spec/policies
27
+ - spec/serializers
28
+
29
+ # Default generators to run after app creation
30
+ generators:
31
+ - model
32
+ - resource
33
+
34
+ # Features to enable by default
35
+ features:
36
+ - authentication
37
+ - authorization
38
+ - api
39
+ - versioning
40
+ - token_auth
41
+
42
+ # Template variables
43
+ defaults:
44
+ testing: rspec
45
+ database: postgresql
46
+ api_format: json
47
+ auth_strategy: doorkeeper