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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +528 -0
- data/bin/railsforge +8 -0
- data/lib/railsforge/analyzers/base_analyzer.rb +41 -0
- data/lib/railsforge/analyzers/controller_analyzer.rb +83 -0
- data/lib/railsforge/analyzers/database_analyzer.rb +55 -0
- data/lib/railsforge/analyzers/metrics_analyzer.rb +55 -0
- data/lib/railsforge/analyzers/model_analyzer.rb +74 -0
- data/lib/railsforge/analyzers/performance_analyzer.rb +161 -0
- data/lib/railsforge/analyzers/refactor_analyzer.rb +118 -0
- data/lib/railsforge/analyzers/security_analyzer.rb +169 -0
- data/lib/railsforge/analyzers/spec_analyzer.rb +58 -0
- data/lib/railsforge/api_generator.rb +397 -0
- data/lib/railsforge/audit.rb +289 -0
- data/lib/railsforge/cli.rb +671 -0
- data/lib/railsforge/config.rb +181 -0
- data/lib/railsforge/database_analyzer.rb +300 -0
- data/lib/railsforge/doctor.rb +250 -0
- data/lib/railsforge/feature_generator.rb +560 -0
- data/lib/railsforge/generator.rb +313 -0
- data/lib/railsforge/generators/base_generator.rb +70 -0
- data/lib/railsforge/generators/demo_generator.rb +307 -0
- data/lib/railsforge/generators/devops_generator.rb +287 -0
- data/lib/railsforge/generators/monitoring_generator.rb +134 -0
- data/lib/railsforge/generators/service_generator.rb +122 -0
- data/lib/railsforge/generators/stimulus_controller_generator.rb +129 -0
- data/lib/railsforge/generators/test_generator.rb +289 -0
- data/lib/railsforge/generators/view_component_generator.rb +169 -0
- data/lib/railsforge/graph.rb +270 -0
- data/lib/railsforge/loader.rb +56 -0
- data/lib/railsforge/mailer_generator.rb +191 -0
- data/lib/railsforge/plugins/plugin_loader.rb +60 -0
- data/lib/railsforge/plugins.rb +30 -0
- data/lib/railsforge/profiles/admin_app.yml +49 -0
- data/lib/railsforge/profiles/api_only.yml +47 -0
- data/lib/railsforge/profiles/blog.yml +47 -0
- data/lib/railsforge/profiles/standard.yml +44 -0
- data/lib/railsforge/profiles.rb +99 -0
- data/lib/railsforge/refactor_analyzer.rb +401 -0
- data/lib/railsforge/refactor_controller.rb +277 -0
- data/lib/railsforge/refactors/refactor_controller.rb +117 -0
- data/lib/railsforge/template_loader.rb +105 -0
- data/lib/railsforge/templates/v1/form/spec_template.rb +18 -0
- data/lib/railsforge/templates/v1/form/template.rb +28 -0
- data/lib/railsforge/templates/v1/job/spec_template.rb +17 -0
- data/lib/railsforge/templates/v1/job/template.rb +13 -0
- data/lib/railsforge/templates/v1/policy/spec_template.rb +41 -0
- data/lib/railsforge/templates/v1/policy/template.rb +57 -0
- data/lib/railsforge/templates/v1/presenter/spec_template.rb +12 -0
- data/lib/railsforge/templates/v1/presenter/template.rb +13 -0
- data/lib/railsforge/templates/v1/query/spec_template.rb +12 -0
- data/lib/railsforge/templates/v1/query/template.rb +16 -0
- data/lib/railsforge/templates/v1/serializer/spec_template.rb +13 -0
- data/lib/railsforge/templates/v1/serializer/template.rb +11 -0
- data/lib/railsforge/templates/v1/service/spec_template.rb +12 -0
- data/lib/railsforge/templates/v1/service/template.rb +25 -0
- data/lib/railsforge/templates/v1/stimulus_controller/template.rb +35 -0
- data/lib/railsforge/templates/v1/view_component/template.rb +24 -0
- data/lib/railsforge/templates/v2/job/template.rb +49 -0
- data/lib/railsforge/templates/v2/query/template.rb +66 -0
- data/lib/railsforge/templates/v2/service/spec_template.rb +33 -0
- data/lib/railsforge/templates/v2/service/template.rb +71 -0
- data/lib/railsforge/templates/v3/job/template.rb +72 -0
- data/lib/railsforge/templates/v3/query/spec_template.rb +54 -0
- data/lib/railsforge/templates/v3/query/template.rb +115 -0
- data/lib/railsforge/templates/v3/service/spec_template.rb +51 -0
- data/lib/railsforge/templates/v3/service/template.rb +84 -0
- data/lib/railsforge/version.rb +5 -0
- data/lib/railsforge/wizard.rb +265 -0
- data/lib/railsforge/wizard_tui.rb +286 -0
- data/lib/railsforge.rb +13 -0
- metadata +216 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
module RailsForge
|
|
2
|
+
# FeatureGenerator module handles generating multiple related files for a feature
|
|
3
|
+
module FeatureGenerator
|
|
4
|
+
# Error class for invalid feature names
|
|
5
|
+
class InvalidFeatureNameError < StandardError; end
|
|
6
|
+
|
|
7
|
+
# Validates the feature name to ensure it's valid Ruby class name
|
|
8
|
+
def self.validate_feature_name(name)
|
|
9
|
+
if name.nil? || name.strip.empty?
|
|
10
|
+
raise InvalidFeatureNameError, "Feature name cannot be empty"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Don't validate names starting with dashes (they're likely flags)
|
|
14
|
+
return if name.start_with?("-")
|
|
15
|
+
|
|
16
|
+
unless name =~ /\A[A-Z][a-zA-Z0-9]*\z/
|
|
17
|
+
raise InvalidFeatureNameError, "Feature name must be in PascalCase (e.g., User)"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Generates multiple related files for a feature
|
|
22
|
+
def self.generate(feature_name, with_spec: true)
|
|
23
|
+
validate_feature_name(feature_name)
|
|
24
|
+
|
|
25
|
+
base_path = find_rails_app_path
|
|
26
|
+
unless base_path
|
|
27
|
+
raise "Not in a Rails application directory"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
results = []
|
|
31
|
+
|
|
32
|
+
# Generate Service
|
|
33
|
+
results << generate_service(base_path, feature_name)
|
|
34
|
+
|
|
35
|
+
# Generate Query
|
|
36
|
+
results << generate_query(base_path, feature_name)
|
|
37
|
+
|
|
38
|
+
# Generate Form
|
|
39
|
+
results << generate_form(base_path, feature_name)
|
|
40
|
+
|
|
41
|
+
# Generate Presenter
|
|
42
|
+
results << generate_presenter(base_path, feature_name)
|
|
43
|
+
|
|
44
|
+
# Generate Policy
|
|
45
|
+
results << generate_policy(base_path, feature_name)
|
|
46
|
+
|
|
47
|
+
# Generate Serializer
|
|
48
|
+
results << generate_serializer(base_path, feature_name)
|
|
49
|
+
|
|
50
|
+
if with_spec
|
|
51
|
+
results << generate_service_spec(base_path, feature_name)
|
|
52
|
+
results << generate_query_spec(base_path, feature_name)
|
|
53
|
+
results << generate_form_spec(base_path, feature_name)
|
|
54
|
+
results << generate_presenter_spec(base_path, feature_name)
|
|
55
|
+
results << generate_policy_spec(base_path, feature_name)
|
|
56
|
+
results << generate_serializer_spec(base_path, feature_name)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
"Feature '#{feature_name}' generated successfully with #{results.count} files!"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.generate_service(base_path, feature_name)
|
|
63
|
+
service_dir = File.join(base_path, "app", "services")
|
|
64
|
+
FileUtils.mkdir_p(service_dir)
|
|
65
|
+
|
|
66
|
+
file_name = "#{feature_name.underscore}_service.rb"
|
|
67
|
+
file_path = File.join(service_dir, file_name)
|
|
68
|
+
|
|
69
|
+
return " Skipping service (already exists)" if File.exist?(file_path)
|
|
70
|
+
|
|
71
|
+
File.write(file_path, service_template(feature_name))
|
|
72
|
+
puts " Created app/services/#{file_name}"
|
|
73
|
+
file_path
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.generate_query(base_path, feature_name)
|
|
77
|
+
query_dir = File.join(base_path, "app", "queries")
|
|
78
|
+
FileUtils.mkdir_p(query_dir)
|
|
79
|
+
|
|
80
|
+
file_name = "find_#{feature_name.underscore}.rb"
|
|
81
|
+
file_path = File.join(query_dir, file_name)
|
|
82
|
+
|
|
83
|
+
return " Skipping query (already exists)" if File.exist?(file_path)
|
|
84
|
+
|
|
85
|
+
File.write(file_path, query_template(feature_name))
|
|
86
|
+
puts " Created app/queries/#{file_name}"
|
|
87
|
+
file_path
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.generate_form(base_path, feature_name)
|
|
91
|
+
form_dir = File.join(base_path, "app", "forms")
|
|
92
|
+
FileUtils.mkdir_p(form_dir)
|
|
93
|
+
|
|
94
|
+
file_name = "#{feature_name.underscore}_form.rb"
|
|
95
|
+
file_path = File.join(form_dir, file_name)
|
|
96
|
+
|
|
97
|
+
return " Skipping form (already exists)" if File.exist?(file_path)
|
|
98
|
+
|
|
99
|
+
File.write(file_path, form_template(feature_name))
|
|
100
|
+
puts " Created app/forms/#{file_name}"
|
|
101
|
+
file_path
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.generate_presenter(base_path, feature_name)
|
|
105
|
+
presenter_dir = File.join(base_path, "app", "presenters")
|
|
106
|
+
FileUtils.mkdir_p(presenter_dir)
|
|
107
|
+
|
|
108
|
+
file_name = "#{feature_name.underscore}_presenter.rb"
|
|
109
|
+
file_path = File.join(presenter_dir, file_name)
|
|
110
|
+
|
|
111
|
+
return " Skipping presenter (already exists)" if File.exist?(file_path)
|
|
112
|
+
|
|
113
|
+
File.write(file_path, presenter_template(feature_name))
|
|
114
|
+
puts " Created app/presenters/#{file_name}"
|
|
115
|
+
file_path
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.generate_policy(base_path, feature_name)
|
|
119
|
+
policy_dir = File.join(base_path, "app", "policies")
|
|
120
|
+
FileUtils.mkdir_p(policy_dir)
|
|
121
|
+
|
|
122
|
+
file_name = "#{feature_name.underscore}_policy.rb"
|
|
123
|
+
file_path = File.join(policy_dir, file_name)
|
|
124
|
+
|
|
125
|
+
return " Skipping policy (already exists)" if File.exist?(file_path)
|
|
126
|
+
|
|
127
|
+
File.write(file_path, policy_template(feature_name))
|
|
128
|
+
puts " Created app/policies/#{file_name}"
|
|
129
|
+
file_path
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def self.generate_serializer(base_path, feature_name)
|
|
133
|
+
serializer_dir = File.join(base_path, "app", "serializers")
|
|
134
|
+
FileUtils.mkdir_p(serializer_dir)
|
|
135
|
+
|
|
136
|
+
file_name = "#{feature_name.underscore}_serializer.rb"
|
|
137
|
+
file_path = File.join(serializer_dir, file_name)
|
|
138
|
+
|
|
139
|
+
return " Skipping serializer (already exists)" if File.exist?(file_path)
|
|
140
|
+
|
|
141
|
+
File.write(file_path, serializer_template(feature_name))
|
|
142
|
+
puts " Created app/serializers/#{file_name}"
|
|
143
|
+
file_path
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Spec file generators
|
|
147
|
+
def self.generate_service_spec(base_path, feature_name)
|
|
148
|
+
spec_dir = File.join(base_path, "spec", "services")
|
|
149
|
+
FileUtils.mkdir_p(spec_dir)
|
|
150
|
+
|
|
151
|
+
file_name = "#{feature_name.underscore}_service_spec.rb"
|
|
152
|
+
file_path = File.join(spec_dir, file_name)
|
|
153
|
+
|
|
154
|
+
return " Skipping service spec (already exists)" if File.exist?(file_path)
|
|
155
|
+
|
|
156
|
+
File.write(file_path, service_spec_template(feature_name))
|
|
157
|
+
puts " Created spec/services/#{file_name}"
|
|
158
|
+
file_path
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def self.generate_query_spec(base_path, feature_name)
|
|
162
|
+
spec_dir = File.join(base_path, "spec", "queries")
|
|
163
|
+
FileUtils.mkdir_p(spec_dir)
|
|
164
|
+
|
|
165
|
+
file_name = "find_#{feature_name.underscore}_spec.rb"
|
|
166
|
+
file_path = File.join(spec_dir, file_name)
|
|
167
|
+
|
|
168
|
+
return " Skipping query spec (already exists)" if File.exist?(file_path)
|
|
169
|
+
|
|
170
|
+
File.write(file_path, query_spec_template(feature_name))
|
|
171
|
+
puts " Created spec/queries/#{file_name}"
|
|
172
|
+
file_path
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.generate_form_spec(base_path, feature_name)
|
|
176
|
+
spec_dir = File.join(base_path, "spec", "forms")
|
|
177
|
+
FileUtils.mkdir_p(spec_dir)
|
|
178
|
+
|
|
179
|
+
file_name = "#{feature_name.underscore}_form_spec.rb"
|
|
180
|
+
file_path = File.join(spec_dir, file_name)
|
|
181
|
+
|
|
182
|
+
return " Skipping form spec (already exists)" if File.exist?(file_path)
|
|
183
|
+
|
|
184
|
+
File.write(file_path, form_spec_template(feature_name))
|
|
185
|
+
puts " Created spec/forms/#{file_name}"
|
|
186
|
+
file_path
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def self.generate_presenter_spec(base_path, feature_name)
|
|
190
|
+
spec_dir = File.join(base_path, "spec", "presenters")
|
|
191
|
+
FileUtils.mkdir_p(spec_dir)
|
|
192
|
+
|
|
193
|
+
file_name = "#{feature_name.underscore}_presenter_spec.rb"
|
|
194
|
+
file_path = File.join(spec_dir, file_name)
|
|
195
|
+
|
|
196
|
+
return " Skipping presenter spec (already exists)" if File.exist?(file_path)
|
|
197
|
+
|
|
198
|
+
File.write(file_path, presenter_spec_template(feature_name))
|
|
199
|
+
puts " Created spec/presenters/#{file_name}"
|
|
200
|
+
file_path
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def self.generate_policy_spec(base_path, feature_name)
|
|
204
|
+
spec_dir = File.join(base_path, "spec", "policies")
|
|
205
|
+
FileUtils.mkdir_p(spec_dir)
|
|
206
|
+
|
|
207
|
+
file_name = "#{feature_name.underscore}_policy_spec.rb"
|
|
208
|
+
file_path = File.join(spec_dir, file_name)
|
|
209
|
+
|
|
210
|
+
return " Skipping policy spec (already exists)" if File.exist?(file_path)
|
|
211
|
+
|
|
212
|
+
File.write(file_path, policy_spec_template(feature_name))
|
|
213
|
+
puts " Created spec/policies/#{file_name}"
|
|
214
|
+
file_path
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def self.generate_serializer_spec(base_path, feature_name)
|
|
218
|
+
spec_dir = File.join(base_path, "spec", "serializers")
|
|
219
|
+
FileUtils.mkdir_p(spec_dir)
|
|
220
|
+
|
|
221
|
+
file_name = "#{feature_name.underscore}_serializer_spec.rb"
|
|
222
|
+
file_path = File.join(spec_dir, file_name)
|
|
223
|
+
|
|
224
|
+
return " Skipping serializer spec (already exists)" if File.exist?(file_path)
|
|
225
|
+
|
|
226
|
+
File.write(file_path, serializer_spec_template(feature_name))
|
|
227
|
+
puts " Created spec/serializers/#{file_name}"
|
|
228
|
+
file_path
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def self.find_rails_app_path
|
|
232
|
+
path = Dir.pwd
|
|
233
|
+
10.times do
|
|
234
|
+
return path if File.exist?(File.join(path, "config", "application.rb"))
|
|
235
|
+
parent = File.dirname(path)
|
|
236
|
+
break if parent == path
|
|
237
|
+
path = parent
|
|
238
|
+
end
|
|
239
|
+
nil
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Templates
|
|
243
|
+
def self.service_template(feature_name)
|
|
244
|
+
<<~RUBY
|
|
245
|
+
# Service class for #{feature_name}
|
|
246
|
+
# Encapsulates business logic
|
|
247
|
+
#
|
|
248
|
+
# Usage:
|
|
249
|
+
# result = #{feature_name}Service.call(params)
|
|
250
|
+
#
|
|
251
|
+
# @example Basic service
|
|
252
|
+
# class Create#{feature_name}Service
|
|
253
|
+
# def initialize(#{feature_name.underscore}_params)
|
|
254
|
+
# @#{feature_name.underscore}_params = #{feature_name.underscore}_params
|
|
255
|
+
# end
|
|
256
|
+
#
|
|
257
|
+
# def call
|
|
258
|
+
# #{feature_name}.create!(@#{feature_name.underscore}_params)
|
|
259
|
+
# end
|
|
260
|
+
# end
|
|
261
|
+
class #{feature_name}Service
|
|
262
|
+
def initialize(#{feature_name.underscore}_params = {})
|
|
263
|
+
@params = #{feature_name.underscore}_params
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def call
|
|
267
|
+
# TODO: Implement service logic
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
RUBY
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def self.query_template(feature_name)
|
|
274
|
+
<<~RUBY
|
|
275
|
+
# Query class for #{feature_name}
|
|
276
|
+
# Encapsulates database queries
|
|
277
|
+
#
|
|
278
|
+
# Usage:
|
|
279
|
+
# result = #{feature_name}Query.call
|
|
280
|
+
# result = #{feature_name}Query.call(scope: #{feature_name}.active)
|
|
281
|
+
class Find#{feature_name}
|
|
282
|
+
def initialize(scope: nil)
|
|
283
|
+
@scope = scope || #{feature_name}.all
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def call
|
|
287
|
+
# TODO: Implement query logic
|
|
288
|
+
@scope
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
RUBY
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def self.form_template(feature_name)
|
|
295
|
+
<<~RUBY
|
|
296
|
+
# Form class for #{feature_name}
|
|
297
|
+
# Encapsulates form validation logic
|
|
298
|
+
#
|
|
299
|
+
# Usage:
|
|
300
|
+
# form = #{feature_name}Form.new(params)
|
|
301
|
+
# if form.valid?
|
|
302
|
+
# form.save
|
|
303
|
+
# end
|
|
304
|
+
class #{feature_name}Form
|
|
305
|
+
include ActiveModel::Model
|
|
306
|
+
|
|
307
|
+
attr_accessor :#{feature_name.underscore}_attributes
|
|
308
|
+
|
|
309
|
+
validate :validate_#{feature_name.underscore}
|
|
310
|
+
|
|
311
|
+
def save
|
|
312
|
+
return false unless valid?
|
|
313
|
+
|
|
314
|
+
# TODO: Implement save logic
|
|
315
|
+
true
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
private
|
|
319
|
+
|
|
320
|
+
def validate_#{feature_name.underscore}
|
|
321
|
+
# TODO: Add validations
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
RUBY
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def self.presenter_template(feature_name)
|
|
328
|
+
<<~RUBY
|
|
329
|
+
# Presenter class for #{feature_name}
|
|
330
|
+
# Handles view presentation logic
|
|
331
|
+
#
|
|
332
|
+
# Usage:
|
|
333
|
+
# presenter = #{feature_name}Presenter.new(@#{feature_name.underscore})
|
|
334
|
+
# presenter.display_name
|
|
335
|
+
class #{feature_name}Presenter
|
|
336
|
+
def initialize(#{feature_name.underscore})
|
|
337
|
+
@#{feature_name.underscore} = #{feature_name.underscore}
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# TODO: Add presenter methods
|
|
341
|
+
end
|
|
342
|
+
RUBY
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def self.policy_template(feature_name)
|
|
346
|
+
<<~RUBY
|
|
347
|
+
# Policy class for #{feature_name}
|
|
348
|
+
# Handles authorization logic
|
|
349
|
+
#
|
|
350
|
+
# Usage:
|
|
351
|
+
# authorize #{feature_name.underscore}
|
|
352
|
+
# policy_scope(#{feature_name.underscore})
|
|
353
|
+
class #{feature_name}Policy
|
|
354
|
+
attr_reader :user, :record
|
|
355
|
+
|
|
356
|
+
def initialize(user, record)
|
|
357
|
+
@user = user
|
|
358
|
+
@record = record
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def index?
|
|
362
|
+
true
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def show?
|
|
366
|
+
true
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def create?
|
|
370
|
+
user.present?
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def new?
|
|
374
|
+
create?
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def update?
|
|
378
|
+
user.present?
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def edit?
|
|
382
|
+
update?
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def destroy?
|
|
386
|
+
user.present?
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
class Scope
|
|
390
|
+
def initialize(user, scope)
|
|
391
|
+
@user = user
|
|
392
|
+
@scope = scope
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def resolve
|
|
396
|
+
scope.all
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
private
|
|
400
|
+
|
|
401
|
+
attr_reader :user, :scope
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
RUBY
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def self.serializer_template(feature_name)
|
|
408
|
+
<<~RUBY
|
|
409
|
+
# Serializer class for #{feature_name}
|
|
410
|
+
# Handles JSON serialization
|
|
411
|
+
#
|
|
412
|
+
# Usage:
|
|
413
|
+
# render json: #{feature_name}Serializer.new(#{feature_name.underscore})
|
|
414
|
+
class #{feature_name}Serializer < ApplicationSerializer
|
|
415
|
+
attributes :id
|
|
416
|
+
|
|
417
|
+
# TODO: Add custom methods
|
|
418
|
+
end
|
|
419
|
+
RUBY
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Spec templates
|
|
423
|
+
def self.service_spec_template(feature_name)
|
|
424
|
+
<<~RUBY
|
|
425
|
+
require 'rails_helper'
|
|
426
|
+
|
|
427
|
+
RSpec.describe #{feature_name}Service do
|
|
428
|
+
let(:params) { {} }
|
|
429
|
+
subject { described_class.new(params) }
|
|
430
|
+
|
|
431
|
+
describe '#call' do
|
|
432
|
+
it 'returns successful result' do
|
|
433
|
+
expect(subject.call).to be_truthy
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
RUBY
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def self.query_spec_template(feature_name)
|
|
441
|
+
<<~RUBY
|
|
442
|
+
require 'rails_helper'
|
|
443
|
+
|
|
444
|
+
RSpec.describe Find#{feature_name} do
|
|
445
|
+
let(:scope) { #{feature_name}.all }
|
|
446
|
+
subject { described_class.new(scope: scope) }
|
|
447
|
+
|
|
448
|
+
describe '#call' do
|
|
449
|
+
it 'returns scope' do
|
|
450
|
+
expect(subject.call).to eq(scope)
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
RUBY
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def self.form_spec_template(feature_name)
|
|
458
|
+
<<~RUBY
|
|
459
|
+
require 'rails_helper'
|
|
460
|
+
|
|
461
|
+
RSpec.describe #{feature_name}Form do
|
|
462
|
+
let(:params) { {} }
|
|
463
|
+
subject { described_class.new(#{feature_name.underscore}_params: params) }
|
|
464
|
+
|
|
465
|
+
describe '#valid?' do
|
|
466
|
+
it 'is valid with valid params' do
|
|
467
|
+
expect(subject.valid?).to be_truthy
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
describe '#save' do
|
|
472
|
+
it 'saves successfully' do
|
|
473
|
+
expect(subject.save).to be_truthy
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
RUBY
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def self.presenter_spec_template(feature_name)
|
|
481
|
+
<<~RUBY
|
|
482
|
+
require 'rails_helper'
|
|
483
|
+
|
|
484
|
+
RSpec.describe #{feature_name}Presenter do
|
|
485
|
+
let(:#{feature_name.underscore}) { #{feature_name}.new }
|
|
486
|
+
subject { described_class.new(#{feature_name.underscore}) }
|
|
487
|
+
|
|
488
|
+
describe 'initialization' do
|
|
489
|
+
it 'initializes successfully' do
|
|
490
|
+
expect(subject).to be_a(described_class)
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
RUBY
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def self.policy_spec_template(feature_name)
|
|
498
|
+
<<~RUBY
|
|
499
|
+
require 'rails_helper'
|
|
500
|
+
|
|
501
|
+
RSpec.describe #{feature_name}Policy do
|
|
502
|
+
let(:user) { nil }
|
|
503
|
+
let(:record) { #{feature_name}.new }
|
|
504
|
+
subject { described_class.new(user, record) }
|
|
505
|
+
|
|
506
|
+
describe '#index?' do
|
|
507
|
+
it 'allows everyone' do
|
|
508
|
+
expect(subject.index?).to be_truthy
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
describe '#show?' do
|
|
513
|
+
it 'allows everyone' do
|
|
514
|
+
expect(subject.show?).to be_truthy
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
describe '#create?' do
|
|
519
|
+
it 'requires user' do
|
|
520
|
+
expect(subject.create?).to be_falsey
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
it 'allows authenticated user' do
|
|
524
|
+
user = User.new
|
|
525
|
+
expect(described_class.new(user, record).create?).to be_truthy
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
describe 'Scope' do
|
|
530
|
+
let(:scope) { #{feature_name}.all }
|
|
531
|
+
subject { described_class::Scope.new(user, scope) }
|
|
532
|
+
|
|
533
|
+
describe '#resolve' do
|
|
534
|
+
it 'returns all records' do
|
|
535
|
+
expect(subject.resolve).to eq(scope.all)
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
RUBY
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def self.serializer_spec_template(feature_name)
|
|
544
|
+
<<~RUBY
|
|
545
|
+
require 'rails_helper'
|
|
546
|
+
|
|
547
|
+
RSpec.describe #{feature_name}Serializer do
|
|
548
|
+
let(:#{feature_name.underscore}) { #{feature_name}.new }
|
|
549
|
+
subject { described_class.new(#{feature_name.underscore}) }
|
|
550
|
+
|
|
551
|
+
describe 'serialization' do
|
|
552
|
+
it 'includes id' do
|
|
553
|
+
expect(subject.as_json[:id]).to eq(#{feature_name.underscore}.id)
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
RUBY
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
end
|