railsforge 1.0.2 → 2.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +105 -444
  3. data/lib/railsforge/analyzers/controller_analyzer.rb +29 -55
  4. data/lib/railsforge/analyzers/database_analyzer.rb +16 -30
  5. data/lib/railsforge/analyzers/metrics_analyzer.rb +8 -22
  6. data/lib/railsforge/analyzers/model_analyzer.rb +29 -46
  7. data/lib/railsforge/analyzers/performance_analyzer.rb +34 -94
  8. data/lib/railsforge/analyzers/refactor_analyzer.rb +77 -57
  9. data/lib/railsforge/analyzers/security_analyzer.rb +34 -91
  10. data/lib/railsforge/analyzers/spec_analyzer.rb +17 -31
  11. data/lib/railsforge/cli.rb +14 -650
  12. data/lib/railsforge/cli_minimal.rb +8 -55
  13. data/lib/railsforge/doctor.rb +52 -225
  14. data/lib/railsforge/formatter.rb +102 -0
  15. data/lib/railsforge/issue.rb +23 -0
  16. data/lib/railsforge/loader.rb +4 -64
  17. data/lib/railsforge/version.rb +1 -1
  18. metadata +14 -82
  19. data/lib/railsforge/api_generator.rb +0 -397
  20. data/lib/railsforge/audit.rb +0 -289
  21. data/lib/railsforge/config.rb +0 -181
  22. data/lib/railsforge/database_analyzer.rb +0 -300
  23. data/lib/railsforge/feature_generator.rb +0 -560
  24. data/lib/railsforge/generator.rb +0 -313
  25. data/lib/railsforge/generators/api_generator.rb +0 -392
  26. data/lib/railsforge/generators/base_generator.rb +0 -75
  27. data/lib/railsforge/generators/demo_generator.rb +0 -307
  28. data/lib/railsforge/generators/devops_generator.rb +0 -287
  29. data/lib/railsforge/generators/form_generator.rb +0 -180
  30. data/lib/railsforge/generators/job_generator.rb +0 -176
  31. data/lib/railsforge/generators/monitoring_generator.rb +0 -134
  32. data/lib/railsforge/generators/policy_generator.rb +0 -220
  33. data/lib/railsforge/generators/presenter_generator.rb +0 -173
  34. data/lib/railsforge/generators/query_generator.rb +0 -174
  35. data/lib/railsforge/generators/serializer_generator.rb +0 -166
  36. data/lib/railsforge/generators/service_generator.rb +0 -122
  37. data/lib/railsforge/generators/stimulus_controller_generator.rb +0 -129
  38. data/lib/railsforge/generators/test_generator.rb +0 -289
  39. data/lib/railsforge/generators/view_component_generator.rb +0 -169
  40. data/lib/railsforge/graph.rb +0 -270
  41. data/lib/railsforge/mailer_generator.rb +0 -191
  42. data/lib/railsforge/plugins/plugin_loader.rb +0 -60
  43. data/lib/railsforge/plugins.rb +0 -30
  44. data/lib/railsforge/profiles.rb +0 -99
  45. data/lib/railsforge/refactor_analyzer.rb +0 -401
  46. data/lib/railsforge/refactor_controller.rb +0 -277
  47. data/lib/railsforge/refactors/refactor_controller.rb +0 -117
  48. data/lib/railsforge/template_loader.rb +0 -105
  49. data/lib/railsforge/templates/v1/form/spec_template.rb +0 -18
  50. data/lib/railsforge/templates/v1/form/template.rb +0 -28
  51. data/lib/railsforge/templates/v1/job/spec_template.rb +0 -17
  52. data/lib/railsforge/templates/v1/job/template.rb +0 -13
  53. data/lib/railsforge/templates/v1/policy/spec_template.rb +0 -41
  54. data/lib/railsforge/templates/v1/policy/template.rb +0 -57
  55. data/lib/railsforge/templates/v1/presenter/spec_template.rb +0 -12
  56. data/lib/railsforge/templates/v1/presenter/template.rb +0 -13
  57. data/lib/railsforge/templates/v1/query/spec_template.rb +0 -12
  58. data/lib/railsforge/templates/v1/query/template.rb +0 -16
  59. data/lib/railsforge/templates/v1/serializer/spec_template.rb +0 -13
  60. data/lib/railsforge/templates/v1/serializer/template.rb +0 -11
  61. data/lib/railsforge/templates/v1/service/spec_template.rb +0 -12
  62. data/lib/railsforge/templates/v1/service/template.rb +0 -25
  63. data/lib/railsforge/templates/v1/stimulus_controller/template.rb +0 -35
  64. data/lib/railsforge/templates/v1/view_component/template.rb +0 -24
  65. data/lib/railsforge/templates/v2/job/template.rb +0 -49
  66. data/lib/railsforge/templates/v2/query/template.rb +0 -66
  67. data/lib/railsforge/templates/v2/service/spec_template.rb +0 -33
  68. data/lib/railsforge/templates/v2/service/template.rb +0 -71
  69. data/lib/railsforge/templates/v3/job/template.rb +0 -72
  70. data/lib/railsforge/templates/v3/query/spec_template.rb +0 -54
  71. data/lib/railsforge/templates/v3/query/template.rb +0 -115
  72. data/lib/railsforge/templates/v3/service/spec_template.rb +0 -51
  73. data/lib/railsforge/templates/v3/service/template.rb +0 -93
  74. data/lib/railsforge/wizard.rb +0 -265
  75. data/lib/railsforge/wizard_tui.rb +0 -286
@@ -1,289 +0,0 @@
1
- # Audit module for RailsForge
2
- # Runs a comprehensive architecture health check
3
-
4
- module RailsForge
5
- # Audit module combines all analyzers to produce a comprehensive report
6
- module Audit
7
- # Error class for audit issues
8
- class AuditError < StandardError; end
9
-
10
- # Default scoring weights for each analyzer
11
- WEIGHTS = {
12
- controllers: 25,
13
- models: 25,
14
- database: 20,
15
- specs: 15,
16
- metrics: 15
17
- }.freeze
18
-
19
- # Run full audit and return results
20
- # @param base_path [String] Rails app root path
21
- # @return [Hash] Complete audit results
22
- def self.run(base_path = nil)
23
- base_path ||= find_rails_app_path
24
- raise AuditError, "Not in a Rails application directory" unless base_path
25
-
26
- results = {
27
- timestamp: Time.now.iso8601,
28
- base_path: base_path,
29
- controllers: run_controller_analysis(base_path),
30
- models: run_model_analysis(base_path),
31
- database: run_database_analysis(base_path),
32
- specs: run_spec_analysis(base_path),
33
- metrics: run_metrics_analysis(base_path)
34
- }
35
-
36
- results[:score] = calculate_score(results)
37
- results
38
- end
39
-
40
- # Run controller analysis
41
- # @param base_path [String] Rails app root path
42
- # @return [Hash] Controller analysis results
43
- def self.run_controller_analysis(base_path)
44
- begin
45
- issues = ControllerAnalyzer.analyze(base_path)
46
- {
47
- issues_count: issues.length,
48
- issues: issues,
49
- status: issues.empty? ? :pass : :warning
50
- }
51
- rescue => e
52
- { issues_count: 0, issues: [], status: :error, error: e.message }
53
- end
54
- end
55
-
56
- # Run model analysis
57
- # @param base_path [String] Rails app root path
58
- # @return [Hash] Model analysis results
59
- def self.run_model_analysis(base_path)
60
- begin
61
- issues = ModelAnalyzer.analyze(base_path)
62
- {
63
- issues_count: issues.length,
64
- issues: issues,
65
- status: issues.empty? ? :pass : :warning
66
- }
67
- rescue => e
68
- { issues_count: 0, issues: [], status: :error, error: e.message }
69
- end
70
- end
71
-
72
- # Run database analysis
73
- # @param base_path [String] Rails app root path
74
- # @return [Hash] Database analysis results
75
- def self.run_database_analysis(base_path)
76
- begin
77
- issues = DatabaseAnalyzer.analyze(base_path)
78
- {
79
- issues_count: issues.length,
80
- issues: issues,
81
- status: issues.empty? ? :pass : :warning
82
- }
83
- rescue => e
84
- { issues_count: 0, issues: [], status: :error, error: e.message }
85
- end
86
- end
87
-
88
- # Run spec analysis
89
- # @param base_path [String] Rails app root path
90
- # @return [Hash] Spec analysis results
91
- def self.run_spec_analysis(base_path)
92
- begin
93
- results = SpecAnalyzer.analyze(base_path)
94
- missing_count = results.count { |r| r[:has_spec] == false }
95
- {
96
- issues_count: missing_count,
97
- coverage: results,
98
- status: missing_count == 0 ? :pass : :warning
99
- }
100
- rescue => e
101
- { issues_count: 0, coverage: [], status: :error, error: e.message }
102
- end
103
- end
104
-
105
- # Run metrics analysis
106
- # @param base_path [String] Rails app root path
107
- # @return [Hash] Metrics analysis results
108
- def self.run_metrics_analysis(base_path)
109
- begin
110
- metrics = MetricsAnalyzer.analyze(base_path)
111
- {
112
- metrics: metrics,
113
- status: :pass
114
- }
115
- rescue => e
116
- { metrics: {}, status: :error, error: e.message }
117
- end
118
- end
119
-
120
- # Calculate overall architecture score
121
- # @param results [Hash] All analysis results
122
- # @return [Integer] Score out of 100
123
- def self.calculate_score(results)
124
- score = 100
125
-
126
- # Deduct points for controller issues
127
- controller_issues = results[:controllers][:issues_count]
128
- score -= [controller_issues * 5, WEIGHTS[:controllers]].min
129
-
130
- # Deduct points for model issues
131
- model_issues = results[:models][:issues_count]
132
- score -= [model_issues * 5, WEIGHTS[:models]].min
133
-
134
- # Deduct points for database issues
135
- db_issues = results[:database][:issues_count]
136
- score -= [db_issues * 3, WEIGHTS[:database]].min
137
-
138
- # Deduct points for missing specs
139
- spec_issues = results[:specs][:issues_count]
140
- score -= [spec_issues * 2, WEIGHTS[:specs]].min
141
-
142
- [score, 0].max.to_i
143
- end
144
-
145
- # Format results as a readable report
146
- # @param results [Hash] Audit results
147
- # @return [String] Formatted report
148
- def self.format_report(results)
149
- score = results[:score]
150
- score_color = score >= 80 ? :green : score >= 50 ? :yellow : :red
151
-
152
- output = []
153
- output << "=" * 60
154
- output << "RailsForge Architecture Audit"
155
- output << "=" * 60
156
- output << ""
157
- output << "Architecture Score: #{colorize(score, score_color)}/100"
158
- output << ""
159
-
160
- # Controllers section
161
- output << "-" * 40
162
- output << "Controllers:"
163
- controller_issues = results[:controllers][:issues_count]
164
- if controller_issues == 0
165
- output << " ✓ No issues found (#{controller_issues})"
166
- else
167
- output << " ⚠ #{controller_issues} issue(s) found"
168
- results[:controllers][:issues].first(5).each do |issue|
169
- output << " - #{issue[:file]}: #{issue[:issues].join(', ')}"
170
- end
171
- output << " ..." if results[:controllers][:issues].count > 5
172
- end
173
- output << ""
174
-
175
- # Models section
176
- output << "-" * 40
177
- output << "Models:"
178
- model_issues = results[:models][:issues_count]
179
- if model_issues == 0
180
- output << " ✓ No issues found (#{model_issues})"
181
- else
182
- output << " ⚠ #{model_issues} issue(s) found"
183
- results[:models][:issues].first(5).each do |issue|
184
- output << " - #{issue[:file]}: #{issue[:issues].join(', ')}"
185
- end
186
- output << " ..." if results[:models][:issues].count > 5
187
- end
188
- output << ""
189
-
190
- # Database section
191
- output << "-" * 40
192
- output << "Database:"
193
- db_issues = results[:database][:issues_count]
194
- if db_issues == 0
195
- output << " ✓ No issues found (#{db_issues})"
196
- else
197
- output << " ⚠ #{db_issues} issue(s) found"
198
- results[:database][:issues].first(5).each do |issue|
199
- output << " - #{issue[:table]}: #{issue[:issue]}"
200
- end
201
- output << " ..." if results[:database][:issues].count > 5
202
- end
203
- output << ""
204
-
205
- # Specs section
206
- output << "-" * 40
207
- output << "Specs:"
208
- spec_issues = results[:specs][:issues_count]
209
- if spec_issues == 0
210
- output << " ✓ All components have specs (#{spec_issues} missing)"
211
- else
212
- output << " ⚠ #{spec_issues} component(s) missing specs"
213
- results[:specs][:coverage].select { |r| r[:has_spec] == false }.first(5).each do |item|
214
- output << " - #{item[:type]}: #{item[:name]}"
215
- end
216
- output << " ..." if results[:specs][:coverage].count { |r| r[:has_spec] == false } > 5
217
- end
218
- output << ""
219
-
220
- # Metrics section
221
- output << "-" * 40
222
- output << "Metrics:"
223
- metrics = results[:metrics][:metrics]
224
- if metrics && !metrics.empty?
225
- output << " Files: #{metrics[:total_files] || 'N/A'}"
226
- output << " Lines: #{metrics[:total_lines] || 'N/A'}"
227
- output << " Services: #{metrics[:services_count] || 0}"
228
- output << " Queries: #{metrics[:queries_count] || 0}"
229
- output << " Jobs: #{metrics[:jobs_count] || 0}"
230
- else
231
- output << " No metrics available"
232
- end
233
- output << ""
234
-
235
- output << "=" * 60
236
- output << "Audit completed at #{results[:timestamp]}"
237
- output << "=" * 60
238
-
239
- output.join("\n")
240
- end
241
-
242
- # Print results to console
243
- # @param results [Hash] Audit results
244
- def self.print_report(results)
245
- puts format_report(results)
246
- end
247
-
248
- # Convert results to JSON
249
- # @param results [Hash] Audit results
250
- # @return [String] JSON string
251
- def self.to_json(results)
252
- require 'json'
253
- # Remove non-serializable data
254
- serializable = results.dup
255
- serializable.delete(:timestamp)
256
- JSON.pretty_generate(serializable)
257
- end
258
-
259
- # Find Rails app path
260
- # @return [String, nil] Rails app root path
261
- def self.find_rails_app_path
262
- path = Dir.pwd
263
- max_depth = 10
264
-
265
- max_depth.times do
266
- return path if File.exist?(File.join(path, "config", "application.rb"))
267
- parent = File.dirname(path)
268
- break if parent == path
269
- path = parent
270
- end
271
-
272
- nil
273
- end
274
-
275
- # Colorize output for terminal
276
- # @param text [String] Text to colorize
277
- # @param color [Symbol] Color name
278
- # @return [String] Colorized text
279
- def self.colorize(text, color)
280
- colors = {
281
- green: "\e[32m",
282
- yellow: "\e[33m",
283
- red: "\e[31m",
284
- reset: "\e[0m"
285
- }
286
- "#{colors[color]}#{text}#{colors[:reset]}"
287
- end
288
- end
289
- end
@@ -1,181 +0,0 @@
1
- # Config module for RailsForge
2
- # Manages .railsforgerc configuration
3
-
4
- require 'yaml'
5
- require 'fileutils'
6
-
7
- module RailsForge
8
- # Config class handles RailsForge configuration
9
- class Config
10
- # Default config file name
11
- CONFIG_FILE = ".railsforgerc".freeze
12
-
13
- # Default configuration
14
- DEFAULT_CONFIG = {
15
- "version" => "1.0",
16
- "profile" => "standard",
17
- "generators" => {
18
- "default_template_version" => "v1",
19
- "include_specs" => true
20
- },
21
- "analyzers" => {
22
- "controller_max_lines" => 150,
23
- "controller_max_methods" => 10,
24
- "model_max_lines" => 200,
25
- "model_max_method_lines" => 15
26
- },
27
- "refactor" => {
28
- "min_method_lines" => 15,
29
- "auto_extract" => false
30
- }
31
- }.freeze
32
-
33
- # Load configuration from file
34
- # @param base_path [String] Rails app root path
35
- # @return [Hash] Configuration
36
- def self.load(base_path = nil)
37
- base_path ||= find_rails_app_path || Dir.pwd
38
-
39
- config_file = File.join(base_path, CONFIG_FILE)
40
-
41
- if File.exist?(config_file)
42
- YAML.safe_load(File.read(config_file), permitted_classes: [], permitted_symbols: [], aliases: true) || DEFAULT_CONFIG
43
- else
44
- DEFAULT_CONFIG.dup
45
- end
46
- end
47
-
48
- # Save configuration to file
49
- # @param config [Hash] Configuration to save
50
- # @param base_path [String] Rails app root path
51
- def self.save(config, base_path = nil)
52
- base_path ||= find_rails_app_path || Dir.pwd
53
-
54
- config_file = File.join(base_path, CONFIG_FILE)
55
-
56
- File.write(config_file, YAML.dump(config))
57
- puts "Configuration saved to #{config_file}"
58
- end
59
-
60
- # Get a specific config value
61
- # @param key [String] Configuration key (dot-notation supported)
62
- # @param base_path [String] Rails app root path
63
- # @return [Object] Configuration value
64
- def self.get(key, base_path = nil)
65
- config = load(base_path)
66
-
67
- keys = key.split(".")
68
- value = config
69
-
70
- keys.each do |k|
71
- value = value[k] if value.is_a?(Hash)
72
- end
73
-
74
- value
75
- end
76
-
77
- # Set a specific config value
78
- # @param key [String] Configuration key
79
- # @param value [Object] Value to set
80
- # @param base_path [String] Rails app root path
81
- def self.set(key, value, base_path = nil)
82
- config = load(base_path)
83
-
84
- keys = key.split(".")
85
- current = config
86
-
87
- keys[0...-1].each do |k|
88
- current[k] ||= {}
89
- current = current[k]
90
- end
91
-
92
- current[keys.last] = value
93
- save(config, base_path)
94
- end
95
-
96
- # Initialize default config file
97
- # @param base_path [String] Rails app root path
98
- def self.init(base_path = nil)
99
- base_path ||= find_rails_app_path || Dir.pwd
100
-
101
- config_file = File.join(base_path, CONFIG_FILE)
102
-
103
- if File.exist?(config_file)
104
- puts "Config file already exists"
105
- return false
106
- end
107
-
108
- save(DEFAULT_CONFIG.dup, base_path)
109
- true
110
- end
111
-
112
- # Show current configuration
113
- # @param base_path [String] Rails app root path
114
- def self.show(base_path = nil)
115
- config = load(base_path)
116
-
117
- puts "RailsForge Configuration:"
118
- puts ""
119
-
120
- # Version
121
- puts " Version: #{config['version']}"
122
- puts " Profile: #{config['profile']}"
123
- puts ""
124
-
125
- # Generators
126
- puts " Generators:"
127
- gens = config["generators"] || {}
128
- gens.each do |key, value|
129
- puts " #{key}: #{value}"
130
- end
131
- puts ""
132
-
133
- # Analyzers
134
- puts " Analyzers:"
135
- analyzers = config["analyzers"] || {}
136
- analyzers.each do |key, value|
137
- puts " #{key}: #{value}"
138
- end
139
- puts ""
140
-
141
- # Refactor
142
- puts " Refactor:"
143
- refactor = config["refactor"] || {}
144
- refactor.each do |key, value|
145
- puts " #{key}: #{value}"
146
- end
147
- end
148
-
149
- # Reset configuration to defaults
150
- # @param base_path [String] Rails app root path
151
- def self.reset(base_path = nil)
152
- base_path ||= find_rails_app_path || Dir.pwd
153
-
154
- config_file = File.join(base_path, CONFIG_FILE)
155
-
156
- if File.exist?(config_file)
157
- File.delete(config_file)
158
- puts "Configuration reset to defaults"
159
- else
160
- puts "No configuration file to reset"
161
- end
162
- end
163
-
164
- private
165
-
166
- # Find Rails app path
167
- def self.find_rails_app_path
168
- path = Dir.pwd
169
- max_depth = 10
170
-
171
- max_depth.times do
172
- return path if File.exist?(File.join(path, "config", "application.rb"))
173
- parent = File.dirname(path)
174
- break if parent == path
175
- path = parent
176
- end
177
-
178
- nil
179
- end
180
- end
181
- end