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,250 @@
1
+ # Doctor module for RailsForge
2
+ # Provides a comprehensive project health report
3
+
4
+ module RailsForge
5
+ # Doctor class provides a full project health check
6
+ class Doctor
7
+ # Initialize the Doctor analyzer
8
+ def initialize(base_path = nil)
9
+ @base_path = base_path || find_rails_app_path
10
+ raise DoctorError, "Not in a Rails application directory" unless @base_path
11
+
12
+ @results = {
13
+ controllers: [],
14
+ models: [],
15
+ database: [],
16
+ specs: [],
17
+ refactors: [],
18
+ metrics: {}
19
+ }
20
+ end
21
+
22
+ # Run all analyzers and generate report
23
+ # @return [Hash] Complete health check results
24
+ def run
25
+ puts "Running RailsForge Doctor..."
26
+ puts ""
27
+
28
+ # Run all analyzers
29
+ run_analyzers
30
+
31
+ # Calculate score
32
+ @results[:score] = calculate_score
33
+
34
+ # Print report
35
+ print_report
36
+
37
+ @results
38
+ end
39
+
40
+ private
41
+
42
+ # Run all available analyzers
43
+ def run_analyzers
44
+ # Controller analysis
45
+ begin
46
+ @results[:controllers] = RailsForge::Analyzers::RefactorAnalyzer.analyze_controllers(@base_path)
47
+ rescue => e
48
+ @results[:controllers] = []
49
+ end
50
+
51
+ # Model analysis
52
+ begin
53
+ @results[:models] = RailsForge::Analyzers::RefactorAnalyzer.analyze_models(@base_path)
54
+ rescue => e
55
+ @results[:models] = []
56
+ end
57
+
58
+ # Database analysis
59
+ begin
60
+ @results[:database] = RailsForge::Analyzers::DatabaseAnalyzer.analyze(@base_path)
61
+ rescue => e
62
+ @results[:database] = []
63
+ end
64
+
65
+ # Spec analysis
66
+ begin
67
+ @results[:specs] = RailsForge::Analyzers::SpecAnalyzer.analyze(@base_path)
68
+ rescue => e
69
+ @results[:specs] = []
70
+ end
71
+
72
+ # Refactor suggestions (from RefactorAnalyzer)
73
+ begin
74
+ @results[:refactors] = []
75
+ @results[:controllers].each do |ctrl|
76
+ @results[:refactors].concat(ctrl[:suggestions] || [])
77
+ end
78
+ @results[:models].each do |model|
79
+ @results[:refactors].concat(model[:suggestions] || [])
80
+ end
81
+ rescue => e
82
+ @results[:refactors] = []
83
+ end
84
+
85
+ # Metrics
86
+ begin
87
+ @results[:metrics] = RailsForge::Analyzers::MetricsAnalyzer.analyze(@base_path)
88
+ rescue => e
89
+ @results[:metrics] = {}
90
+ end
91
+ end
92
+
93
+ # Calculate architecture score
94
+ # @return [Integer] Score out of 100
95
+ def calculate_score
96
+ score = 100
97
+
98
+ # Subtract 5 points per fat controller
99
+ fat_controllers = @results[:controllers].count { |c| c[:needs_refactoring] }
100
+ score -= fat_controllers * 5
101
+
102
+ # Subtract 5 points per fat model
103
+ fat_models = @results[:models].count { |m| m[:needs_refactoring] }
104
+ score -= fat_models * 5
105
+
106
+ # Subtract 3 points per missing spec
107
+ missing_specs = @results[:specs].count { |s| s[:has_spec] == false }
108
+ score -= missing_specs * 3
109
+
110
+ # Subtract 2 points per missing index (database issues)
111
+ missing_indexes = @results[:database].count { |d| d[:type] == :index }
112
+ score -= missing_indexes * 2
113
+
114
+ # Subtract 2 points per long method
115
+ long_methods = count_long_methods
116
+ score -= long_methods * 2
117
+
118
+ [score, 0].max
119
+ end
120
+
121
+ # Count long methods in controllers and models
122
+ # @return [Integer] Number of long methods
123
+ def count_long_methods
124
+ count = 0
125
+
126
+ @results[:controllers].each do |ctrl|
127
+ methods = ctrl[:methods] || []
128
+ count += methods.count { |m| m[:lines].to_i > 15 }
129
+ end
130
+
131
+ @results[:models].each do |model|
132
+ methods = model[:methods] || []
133
+ count += methods.count { |m| m[:lines].to_i > 15 }
134
+ end
135
+
136
+ count
137
+ end
138
+
139
+ # Print formatted report
140
+ def print_report
141
+ score = @results[:score]
142
+ score_color = score >= 80 ? :green : score >= 50 ? :yellow : :red
143
+
144
+ puts "RailsForge Doctor Report"
145
+ puts "────────────────────────"
146
+ puts ""
147
+ puts "Architecture Score: #{colorize(score, score_color)}/100"
148
+ puts ""
149
+
150
+ # Controllers
151
+ puts "Controllers:"
152
+ if @results[:controllers].empty?
153
+ puts " ✓ No issues found"
154
+ else
155
+ @results[:controllers].each do |ctrl|
156
+ issues = ctrl[:issues] || []
157
+ if issues.any?
158
+ puts " ⚠ #{ctrl[:file]}: #{issues.join(', ')}"
159
+ end
160
+ end
161
+ end
162
+ puts ""
163
+
164
+ # Models
165
+ puts "Models:"
166
+ if @results[:models].empty?
167
+ puts " ✓ No issues found"
168
+ else
169
+ @results[:models].each do |model|
170
+ issues = model[:issues] || []
171
+ if issues.any?
172
+ puts " ⚠ #{model[:file]}: #{issues.join(', ')}"
173
+ end
174
+ end
175
+ end
176
+ puts ""
177
+
178
+ # Database
179
+ puts "Database:"
180
+ if @results[:database].empty?
181
+ puts " ✓ No issues found"
182
+ else
183
+ @results[:database].first(5).each do |db|
184
+ puts " ⚠ #{db[:table]}: #{db[:issue]}"
185
+ end
186
+ puts " ... and #{@results[:database].count - 5} more" if @results[:database].count > 5
187
+ end
188
+ puts ""
189
+
190
+ # Specs
191
+ missing_specs = @results[:specs].select { |s| s[:has_spec] == false }
192
+ puts "Specs:"
193
+ if missing_specs.empty?
194
+ puts " ✓ All components have specs"
195
+ else
196
+ puts " ⚠ #{missing_specs.count} missing specs:"
197
+ missing_specs.first(5).each do |spec|
198
+ puts " - #{spec[:type]}: #{spec[:name]}"
199
+ end
200
+ puts " ... and #{missing_specs.count - 5} more" if missing_specs.count > 5
201
+ end
202
+ puts ""
203
+
204
+ # Suggestions
205
+ puts "Suggestions:"
206
+ if @results[:refactors].empty?
207
+ puts " ✓ No refactoring suggestions"
208
+ else
209
+ @results[:refactors].first(10).each do |suggestion|
210
+ puts " • #{suggestion}"
211
+ end
212
+ puts " ... and #{@results[:refactors].count - 10} more" if @results[:refactors].count > 10
213
+ end
214
+ puts ""
215
+ end
216
+
217
+ # Find Rails app path
218
+ # @return [String, nil] Rails app root path
219
+ def find_rails_app_path
220
+ path = Dir.pwd
221
+ max_depth = 10
222
+
223
+ max_depth.times do
224
+ return path if File.exist?(File.join(path, "config", "application.rb"))
225
+ parent = File.dirname(path)
226
+ break if parent == path
227
+ path = parent
228
+ end
229
+
230
+ nil
231
+ end
232
+
233
+ # Colorize output for terminal
234
+ # @param text [String] Text to colorize
235
+ # @param color [Symbol] Color name
236
+ # @return [String] Colorized text
237
+ def colorize(text, color)
238
+ colors = {
239
+ green: "\e[32m",
240
+ yellow: "\e[33m",
241
+ red: "\e[31m",
242
+ reset: "\e[0m"
243
+ }
244
+ "#{colors[color]}#{text}#{colors[:reset]}"
245
+ end
246
+
247
+ # Error class for doctor issues
248
+ class DoctorError < StandardError; end
249
+ end
250
+ end