railsforge 1.0.2 → 2.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +129 -435
  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 +28 -645
  12. data/lib/railsforge/cli_minimal.rb +10 -55
  13. data/lib/railsforge/diff.rb +57 -0
  14. data/lib/railsforge/doctor.rb +52 -225
  15. data/lib/railsforge/formatter.rb +128 -0
  16. data/lib/railsforge/issue.rb +23 -0
  17. data/lib/railsforge/loader.rb +5 -64
  18. data/lib/railsforge/version.rb +1 -1
  19. metadata +15 -82
  20. data/lib/railsforge/api_generator.rb +0 -397
  21. data/lib/railsforge/audit.rb +0 -289
  22. data/lib/railsforge/config.rb +0 -181
  23. data/lib/railsforge/database_analyzer.rb +0 -300
  24. data/lib/railsforge/feature_generator.rb +0 -560
  25. data/lib/railsforge/generator.rb +0 -313
  26. data/lib/railsforge/generators/api_generator.rb +0 -392
  27. data/lib/railsforge/generators/base_generator.rb +0 -75
  28. data/lib/railsforge/generators/demo_generator.rb +0 -307
  29. data/lib/railsforge/generators/devops_generator.rb +0 -287
  30. data/lib/railsforge/generators/form_generator.rb +0 -180
  31. data/lib/railsforge/generators/job_generator.rb +0 -176
  32. data/lib/railsforge/generators/monitoring_generator.rb +0 -134
  33. data/lib/railsforge/generators/policy_generator.rb +0 -220
  34. data/lib/railsforge/generators/presenter_generator.rb +0 -173
  35. data/lib/railsforge/generators/query_generator.rb +0 -174
  36. data/lib/railsforge/generators/serializer_generator.rb +0 -166
  37. data/lib/railsforge/generators/service_generator.rb +0 -122
  38. data/lib/railsforge/generators/stimulus_controller_generator.rb +0 -129
  39. data/lib/railsforge/generators/test_generator.rb +0 -289
  40. data/lib/railsforge/generators/view_component_generator.rb +0 -169
  41. data/lib/railsforge/graph.rb +0 -270
  42. data/lib/railsforge/mailer_generator.rb +0 -191
  43. data/lib/railsforge/plugins/plugin_loader.rb +0 -60
  44. data/lib/railsforge/plugins.rb +0 -30
  45. data/lib/railsforge/profiles.rb +0 -99
  46. data/lib/railsforge/refactor_analyzer.rb +0 -401
  47. data/lib/railsforge/refactor_controller.rb +0 -277
  48. data/lib/railsforge/refactors/refactor_controller.rb +0 -117
  49. data/lib/railsforge/template_loader.rb +0 -105
  50. data/lib/railsforge/templates/v1/form/spec_template.rb +0 -18
  51. data/lib/railsforge/templates/v1/form/template.rb +0 -28
  52. data/lib/railsforge/templates/v1/job/spec_template.rb +0 -17
  53. data/lib/railsforge/templates/v1/job/template.rb +0 -13
  54. data/lib/railsforge/templates/v1/policy/spec_template.rb +0 -41
  55. data/lib/railsforge/templates/v1/policy/template.rb +0 -57
  56. data/lib/railsforge/templates/v1/presenter/spec_template.rb +0 -12
  57. data/lib/railsforge/templates/v1/presenter/template.rb +0 -13
  58. data/lib/railsforge/templates/v1/query/spec_template.rb +0 -12
  59. data/lib/railsforge/templates/v1/query/template.rb +0 -16
  60. data/lib/railsforge/templates/v1/serializer/spec_template.rb +0 -13
  61. data/lib/railsforge/templates/v1/serializer/template.rb +0 -11
  62. data/lib/railsforge/templates/v1/service/spec_template.rb +0 -12
  63. data/lib/railsforge/templates/v1/service/template.rb +0 -25
  64. data/lib/railsforge/templates/v1/stimulus_controller/template.rb +0 -35
  65. data/lib/railsforge/templates/v1/view_component/template.rb +0 -24
  66. data/lib/railsforge/templates/v2/job/template.rb +0 -49
  67. data/lib/railsforge/templates/v2/query/template.rb +0 -66
  68. data/lib/railsforge/templates/v2/service/spec_template.rb +0 -33
  69. data/lib/railsforge/templates/v2/service/template.rb +0 -71
  70. data/lib/railsforge/templates/v3/job/template.rb +0 -72
  71. data/lib/railsforge/templates/v3/query/spec_template.rb +0 -54
  72. data/lib/railsforge/templates/v3/query/template.rb +0 -115
  73. data/lib/railsforge/templates/v3/service/spec_template.rb +0 -51
  74. data/lib/railsforge/templates/v3/service/template.rb +0 -93
  75. data/lib/railsforge/wizard.rb +0 -265
  76. data/lib/railsforge/wizard_tui.rb +0 -286
@@ -1,75 +0,0 @@
1
- # Base generator class for RailsForge
2
- # All generators inherit from this class
3
-
4
- require 'fileutils'
5
-
6
- module RailsForge
7
- module Generators
8
- # Base class for all generators
9
- class BaseGenerator
10
- # Error class for invalid names
11
- class InvalidNameError < StandardError; end
12
-
13
- # Default template version
14
- TEMPLATE_VERSION = "v1".freeze
15
-
16
- # Initialize generator
17
- # @param name [String] Name for the generated object
18
- # @param options [Hash] Generator options
19
- def initialize(name, options = {})
20
- @name = name
21
- @options = options
22
- @base_path = find_rails_app_path
23
- end
24
-
25
- # Find Rails app path
26
- # @return [String, nil] Rails app root path
27
- def find_rails_app_path
28
- path = Dir.pwd
29
- max_depth = 10
30
-
31
- max_depth.times do
32
- return path if File.exist?(File.join(path, "config", "application.rb"))
33
- parent = File.dirname(path)
34
- break if parent == path
35
- path = parent
36
- end
37
-
38
- nil
39
- end
40
-
41
- # Validate name
42
- # @param name [String] Name to validate
43
- # @param pattern [Regexp] Pattern to match
44
- # @param error_class [Class] Error class to raise
45
- def validate_name!(name, pattern, error_class)
46
- raise error_class, "#{self.class.name.split('::').last} name cannot be empty" if name.nil? || name.strip.empty?
47
- raise error_class, "Name must match pattern: #{pattern}" unless name =~ pattern
48
- end
49
-
50
- # Convert to underscore
51
- # @return [String] Underscored name
52
- def underscore
53
- @name.to_s.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
54
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
55
- .downcase
56
- end
57
-
58
- # Convert to camelize
59
- # @return [String] Camelized name
60
- def camelize
61
- result = @name.to_s.gsub(/::/, '/')
62
- result.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
63
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
64
- .split('_')
65
- .map(&:capitalize)
66
- .join
67
- end
68
-
69
- # Generate the file
70
- def generate
71
- raise NotImplementedError, "Subclasses must implement #generate"
72
- end
73
- end
74
- end
75
- end
@@ -1,307 +0,0 @@
1
- # Demo project generator for RailsForge
2
- # Creates a complete demo Rails project
3
-
4
- require 'fileutils'
5
-
6
- module RailsForge
7
- module Generators
8
- # Demo generator creates a sample Rails project
9
- class DemoGenerator < BaseGenerator
10
- # Initialize the generator
11
- def initialize(name, options = {})
12
- super(name, options)
13
- end
14
-
15
- # Generate demo project
16
- def generate
17
- return "Not in a Rails application directory" unless @base_path
18
-
19
- results = []
20
- results << create_readme
21
- results << create_sample_services
22
- results << create_sample_queries
23
- results << create_sample_jobs
24
- results << create_sample_serializers
25
- results << create_sample_policies
26
- results << create_sample_forms
27
-
28
- results.join("\n")
29
- end
30
-
31
- private
32
-
33
- # Create README.md
34
- def create_readme
35
- readme_path = File.join(@base_path, "README.md")
36
- return "Skipping README (already exists)" if File.exist?(readme_path)
37
-
38
- content = <<~MD
39
- # #{@name.titleize}
40
-
41
- Generated with RailsForge
42
-
43
- ## Setup
44
-
45
- ```bash
46
- bundle install
47
- rails db:create db:migrate
48
- rails server
49
- ```
50
-
51
- ## Available Generators
52
-
53
- Run `railsforge generate --help` to see all available generators.
54
-
55
- ### Services
56
- ```bash
57
- railsforge generate service UserService
58
- ```
59
-
60
- ### Queries
61
- ```bash
62
- railsforge generate query FindUsers
63
- ```
64
-
65
- ### Jobs
66
- ```bash
67
- railsforge generate job ProcessData
68
- ```
69
-
70
- ## Analyzers
71
-
72
- Run code analysis:
73
- ```bash
74
- railsforge analyze
75
- railsforge analyze security
76
- railsforge analyze performance
77
- ```
78
-
79
- ## Project Structure
80
-
81
- - `app/services/` - Service objects
82
- - `app/queries/` - Query objects
83
- - `app/jobs/` - Background jobs
84
- - `app/policies/` - Policy objects
85
- - `app/forms/` - Form objects
86
- - `app/serializers/` - JSON serializers
87
- MD
88
-
89
- File.write(readme_path, content)
90
- "Created README.md"
91
- end
92
-
93
- # Create sample services
94
- def create_sample_services
95
- service_dir = File.join(@base_path, "app", "services")
96
- FileUtils.mkdir_p(service_dir)
97
-
98
- samples = %w[user_service notification_service payment_service]
99
- created = []
100
-
101
- samples.each do |name|
102
- path = File.join(service_dir, "#{name}.rb")
103
- next if File.exist?(path)
104
-
105
- File.write(path, service_template(name.classify))
106
- created << name
107
- end
108
-
109
- created.any? ? "Created #{created.count} sample services" : "Services already exist"
110
- end
111
-
112
- # Create sample queries
113
- def create_sample_queries
114
- query_dir = File.join(@base_path, "app", "queries")
115
- FileUtils.mkdir_p(query_dir)
116
-
117
- samples = %w[find_active_users find_recent_orders]
118
- created = []
119
-
120
- samples.each do |name|
121
- path = File.join(query_dir, "#{name}.rb")
122
- next if File.exist?(path)
123
-
124
- File.write(path, query_template(name.classify))
125
- created << name
126
- end
127
-
128
- created.any? ? "Created #{created.count} sample queries" : "Queries already exist"
129
- end
130
-
131
- # Create sample jobs
132
- def create_sample_jobs
133
- job_dir = File.join(@base_path, "app", "jobs")
134
- FileUtils.mkdir_p(job_dir)
135
-
136
- samples = %w[process_order_job send_email_job]
137
- created = []
138
-
139
- samples.each do |name|
140
- path = File.join(job_dir, "#{name}.rb")
141
- next if File.exist?(path)
142
-
143
- File.write(path, job_template(name.classify))
144
- created << name
145
- end
146
-
147
- created.any? ? "Created #{created.count} sample jobs" : "Jobs already exist"
148
- end
149
-
150
- # Create sample serializers
151
- def create_sample_serializers
152
- serializer_dir = File.join(@base_path, "app", "serializers")
153
- FileUtils.mkdir_p(serializer_dir)
154
-
155
- samples = %w[user_serializer article_serializer]
156
- created = []
157
-
158
- samples.each do |name|
159
- path = File.join(serializer_dir, "#{name}.rb")
160
- next if File.exist?(path)
161
-
162
- File.write(path, serializer_template(name.classify))
163
- created << name
164
- end
165
-
166
- created.any? ? "Created #{created.count} sample serializers" : "Serializers already exist"
167
- end
168
-
169
- # Create sample policies
170
- def create_sample_policies
171
- policy_dir = File.join(@base_path, "app", "policies")
172
- FileUtils.mkdir_p(policy_dir)
173
-
174
- samples = %w[user_policy article_policy]
175
- created = []
176
-
177
- samples.each do |name|
178
- path = File.join(policy_dir, "#{name}.rb")
179
- next if File.exist?(path)
180
-
181
- File.write(path, policy_template(name.classify))
182
- created << name
183
- end
184
-
185
- created.any? ? "Created #{created.count} sample policies" : "Policies already exist"
186
- end
187
-
188
- # Create sample forms
189
- def create_sample_forms
190
- form_dir = File.join(@base_path, "app", "forms")
191
- FileUtils.mkdir_p(form_dir)
192
-
193
- samples = %w[contact_form signup_form]
194
- created = []
195
-
196
- samples.each do |name|
197
- path = File.join(form_dir, "#{name}.rb")
198
- next if File.exist?(path)
199
-
200
- File.write(path, form_template(name.classify))
201
- created << name
202
- end
203
-
204
- created.any? ? "Created #{created.count} sample forms" : "Forms already exist"
205
- end
206
-
207
- # Templates
208
- def service_template(name)
209
- <<~RUBY
210
- class #{name}
211
- def initialize(params = {})
212
- @params = params
213
- end
214
-
215
- def call
216
- # TODO: Implement service logic
217
- { success: true }
218
- end
219
-
220
- def self.call(params = {})
221
- new(params).call
222
- end
223
- end
224
- RUBY
225
- end
226
-
227
- def query_template(name)
228
- <<~RUBY
229
- class #{name}
230
- def initialize(relation = nil)
231
- @relation = relation || default_scope
232
- end
233
-
234
- def call
235
- @relation
236
- end
237
-
238
- private
239
-
240
- def default_scope
241
- # TODO: Replace with actual model
242
- OpenStruct.all
243
- end
244
- end
245
- RUBY
246
- end
247
-
248
- def job_template(name)
249
- <<~RUBY
250
- class #{name} < ApplicationJob
251
- queue_as :default
252
-
253
- def perform(*args)
254
- # TODO: Implement job logic
255
- end
256
- end
257
- RUBY
258
- end
259
-
260
- def serializer_template(name)
261
- <<~RUBY
262
- class #{name} < ActiveModel::Serializer
263
- attributes :id, :created_at
264
-
265
- # TODO: Add attributes
266
- end
267
- RUBY
268
- end
269
-
270
- def policy_template(name)
271
- <<~RUBY
272
- class #{name} < ApplicationPolicy
273
- def index?
274
- true
275
- end
276
-
277
- def show?
278
- true
279
- end
280
-
281
- def create?
282
- user.present?
283
- end
284
- end
285
- RUBY
286
- end
287
-
288
- def form_template(name)
289
- <<~RUBY
290
- class #{name} < ApplicationForm
291
- attr_accessor :name, :email
292
-
293
- validates :name, presence: true
294
- validates :email, presence: true
295
-
296
- def save
297
- return false unless valid?
298
-
299
- # TODO: Implement save logic
300
- true
301
- end
302
- end
303
- RUBY
304
- end
305
- end
306
- end
307
- end
@@ -1,287 +0,0 @@
1
- # DevOps generator for RailsForge
2
- # Generates Dockerfile, docker-compose, and CI/CD configs
3
-
4
- require 'fileutils'
5
-
6
- module RailsForge
7
- module Generators
8
- # DevOps generator
9
- class DevopsGenerator < BaseGenerator
10
- # Initialize the generator
11
- def initialize(name = "app", options = {})
12
- super(name, options)
13
- @docker_image = options[:docker_image] || "railsforge/#{name}"
14
- @ci_provider = options[:ci_provider] || "github"
15
- @ruby_version = options[:ruby_version] || "3.2.0"
16
- @node_version = options[:node_version] || "18"
17
- end
18
-
19
- # Generate DevOps files
20
- def generate
21
- return "Not in a Rails application directory" unless @base_path
22
-
23
- results = []
24
- results << create_dockerfile
25
- results << create_docker_compose
26
- results << create_dockerignore
27
- results << create_github_workflow if @ci_provider == "github"
28
- results << create_gitlab_ci if @ci_provider == "gitlab"
29
-
30
- results.join("\n")
31
- end
32
-
33
- private
34
-
35
- # Create Dockerfile
36
- def create_dockerfile
37
- dockerfile_path = File.join(@base_path, "Dockerfile")
38
- return "Skipping Dockerfile (already exists)" if File.exist?(dockerfile_path)
39
-
40
- content = <<~DOCKERFILE
41
- # Ruby version
42
- FROM ruby:#{@ruby_version}-slim
43
-
44
- # Install system dependencies
45
- RUN apt-get update && apt-get install -y \\
46
- build-essential \\
47
- libpq-dev \\
48
- curl \\
49
- git \\
50
- nodejs \\
51
- npm \\
52
- && rm -rf /var/lib/apt/lists/*
53
-
54
- # Set working directory
55
- WORKDIR /app
56
-
57
- # Copy Gemfile
58
- COPY Gemfile Gemfile.lock ./
59
-
60
- # Install gems
61
- RUN bundle install --jobs 4 --retry 3
62
-
63
- # Copy package.json for assets
64
- COPY package.json package-lock.json ./
65
- RUN npm install --production
66
-
67
- # Copy application code
68
- COPY . .
69
-
70
- # Precompile assets
71
- RUN RAILS_ENV=production SECRET_KEY_BASE=dummy bundle exec rails assets:precompile
72
-
73
- # Expose port
74
- EXPOSE 3000
75
-
76
- # Start server
77
- CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
78
- DOCKERFILE
79
-
80
- File.write(dockerfile_path, content)
81
- "Created Dockerfile"
82
- end
83
-
84
- # Create docker-compose.yml
85
- def create_docker_compose
86
- compose_path = File.join(@base_path, "docker-compose.yml")
87
- return "Skipping docker-compose.yml (already exists)" if File.exist?(compose_path)
88
-
89
- content = <<~YAML
90
- version: '3.8'
91
-
92
- services:
93
- app:
94
- build: .
95
- ports:
96
- - "3000:3000"
97
- environment:
98
- - RAILS_ENV=development
99
- - DATABASE_HOST=postgres
100
- - REDIS_HOST=redis
101
- depends_on:
102
- - postgres
103
- - redis
104
- volumes:
105
- - .:/app
106
-
107
- postgres:
108
- image: postgres:15-alpine
109
- environment:
110
- - POSTGRES_USER=rails
111
- - POSTGRES_PASSWORD=password
112
- - POSTGRES_DB=app_development
113
- ports:
114
- - "5432:5432"
115
- volumes:
116
- - postgres_data:/var/lib/postgresql/data
117
-
118
- redis:
119
- image: redis:7-alpine
120
- ports:
121
- - "6379:6379"
122
-
123
- sidekiq:
124
- build: .
125
- command: bundle exec sidekiq
126
- environment:
127
- - RAILS_ENV=development
128
- - DATABASE_HOST=postgres
129
- - REDIS_HOST=redis
130
- depends_on:
131
- - postgres
132
- - redis
133
- volumes:
134
- - .:/app
135
-
136
- volumes:
137
- postgres_data:
138
- YAML
139
-
140
- File.write(compose_path, content)
141
- "Created docker-compose.yml"
142
- end
143
-
144
- # Create .dockerignore
145
- def create_dockerignore
146
- ignore_path = File.join(@base_path, ".dockerignore")
147
- return "Skipping .dockerignore (already exists)" if File.exist?(ignore_path)
148
-
149
- content = <<~IGNORE
150
- .git
151
- .github
152
- .gitignore
153
- log/*.log
154
- tmp/
155
- .bundle/
156
- node_modules/
157
- public/assets/
158
- .DS_Store
159
- *.swp
160
- *.swo
161
- IGNORE
162
-
163
- File.write(ignore_path, content)
164
- "Created .dockerignore"
165
- end
166
-
167
- # Create GitHub Actions workflow
168
- def create_github_workflow
169
- workflow_dir = File.join(@base_path, ".github", "workflows")
170
- FileUtils.mkdir_p(workflow_dir)
171
-
172
- workflow_path = File.join(workflow_dir, "ci.yml")
173
- return "Skipping GitHub workflow (already exists)" if File.exist?(workflow_path)
174
-
175
- content = <<~YAML
176
- name: CI
177
-
178
- on:
179
- push:
180
- branches: [main, develop]
181
- pull_request:
182
- branches: [main, develop]
183
-
184
- jobs:
185
- test:
186
- runs-on: ubuntu-latest
187
- services:
188
- postgres:
189
- image: postgres:15-alpine
190
- env:
191
- POSTGRES_USER: rails
192
- POSTGRES_PASSWORD: password
193
- POSTGRES_DB: app_test
194
- ports:
195
- - 5432:5432
196
- redis:
197
- image: redis:7-alpine
198
- ports:
199
- - 6379:6379
200
-
201
- steps:
202
- - uses: actions/checkout@v3
203
- - name: Set up Ruby
204
- uses: ruby/setup-ruby@v1
205
- with:
206
- ruby-version: #{@ruby_version}
207
- bundler-cache: true
208
- - name: Install dependencies
209
- run: |
210
- bundle install
211
- npm install
212
- - name: Setup database
213
- env:
214
- RAILS_ENV: test
215
- POSTGRES_HOST: localhost
216
- POSTGRES_USER: rails
217
- POSTGRES_PASSWORD: password
218
- POSTGRES_DB: app_test
219
- run: bundle exec rails db:create db:schema:load
220
- - name: Run tests
221
- env:
222
- RAILS_ENV: test
223
- POSTGRES_HOST: localhost
224
- POSTGRES_USER: rails
225
- POSTGRES_PASSWORD: password
226
- POSTGRES_DB: app_test
227
- run: bundle exec rspec
228
- - name: Run linter
229
- run: bundle exec rubocop
230
- YAML
231
-
232
- File.write(workflow_path, content)
233
- "Created GitHub Actions workflow"
234
- end
235
-
236
- # Create GitLab CI config
237
- def create_gitlab_ci
238
- ci_path = File.join(@base_path, ".gitlab-ci.yml")
239
- return "Skipping .gitlab-ci.yml (already exists)" if File.exist?(ci_path)
240
-
241
- content = <<~YAML
242
- stages:
243
- - test
244
- - lint
245
- - deploy
246
-
247
- variables:
248
- RUBY_VERSION: #{@ruby_version}
249
- POSTGRES_DB: app_test
250
- POSTGRES_USER: rails
251
- POSTGRES_PASSWORD: password
252
-
253
- test:
254
- stage: test
255
- image: ruby:#{@ruby_version}
256
- services:
257
- - postgres:15-alpine
258
- - redis:7-alpine
259
- before_script:
260
- - apt-get update -qq && apt-get install -y -qq nodejs npm
261
- - bundle install
262
- - npm install
263
- script:
264
- - RAILS_ENV=test bundle exec rails db:create db:schema:load
265
- - bundle exec rspec
266
- only:
267
- - main
268
- - develop
269
-
270
- rubocop:
271
- stage: lint
272
- image: ruby:#{@ruby_version}
273
- before_script:
274
- - bundle install
275
- script:
276
- - bundle exec rubocop
277
- only:
278
- - main
279
- - develop
280
- YAML
281
-
282
- File.write(ci_path, content)
283
- "Created GitLab CI config"
284
- end
285
- end
286
- end
287
- end