tng 0.1.6 → 0.2.2

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.
@@ -0,0 +1,320 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "extract_methods"
4
+
5
+ module Tng
6
+ module Services
7
+ class DirectGeneration
8
+ include ExtractMethods
9
+
10
+ def initialize(pastel, testng, http_client, params, show_post_generation_menu_proc)
11
+ @pastel = pastel
12
+ @testng = testng
13
+ @http_client = http_client
14
+ @params = params
15
+ @show_post_generation_menu = show_post_generation_menu_proc
16
+ end
17
+
18
+ def run
19
+ type = normalize_type(@params[:type])
20
+ file_name = @params[:file]
21
+ method_name = @params[:method]
22
+
23
+ unless type && file_name && method_name
24
+ puts @pastel.red("❌ All parameters are required for direct generation: --type, --file, --method")
25
+ puts @pastel.yellow("Usage: bundle exec tng --type=controller --file=users --method=index")
26
+ puts @pastel.yellow("Shortcuts: bundle exec tng -t=c -f=users -m=index")
27
+ return
28
+ end
29
+
30
+ unless %w[controller model service other].include?(type)
31
+ puts @pastel.red("❌ Invalid type: #{@params[:type]}")
32
+ puts @pastel.yellow("Supported types: controller, model, service, other")
33
+ return
34
+ end
35
+
36
+ case type
37
+ when "controller"
38
+ run_controller_method_generation(file_name, method_name)
39
+ when "model"
40
+ run_model_method_generation(file_name, method_name)
41
+ when "service"
42
+ run_service_method_generation(file_name, method_name)
43
+ when "other"
44
+ run_other_method_generation(file_name, method_name)
45
+ end
46
+ end
47
+
48
+ def normalize_type(type_param)
49
+ case type_param&.downcase
50
+ when "c", "controller"
51
+ "controller"
52
+ when "m", "mo", "model"
53
+ "model"
54
+ when "s", "se", "service"
55
+ "service"
56
+ when "o", "other"
57
+ "other"
58
+ else
59
+ type_param&.downcase
60
+ end
61
+ end
62
+
63
+ def run_controller_method_generation(file_name, method_name)
64
+ controllers = Tng::Analyzers::Controller.files_in_dir("app/controllers").map do |file|
65
+ relative_path = file[:path].gsub(%r{^.*app/controllers/}, "").gsub(".rb", "")
66
+ namespaced_name = relative_path.split("/").map(&:camelize).join("::")
67
+
68
+ {
69
+ name: namespaced_name,
70
+ path: file[:path]
71
+ }
72
+ end
73
+
74
+ controller = find_matching_controller(controllers, file_name)
75
+
76
+ unless controller
77
+ puts @pastel.red("❌ Controller not found: #{file_name}")
78
+ puts @pastel.yellow("Available controllers:")
79
+ controllers.first(5).each do |ctrl|
80
+ puts @pastel.dim(" - #{ctrl[:name]}")
81
+ end
82
+ puts @pastel.dim(" ... and #{controllers.length - 5} more") if controllers.length > 5
83
+ return
84
+ end
85
+
86
+ methods = extract_controller_methods(controller)
87
+
88
+ if methods.empty?
89
+ puts @pastel.yellow("⚠️ No methods found in #{controller[:name]}")
90
+ return
91
+ end
92
+
93
+ method_info = methods.find { |method| method[:name].downcase == method_name.downcase }
94
+
95
+ unless method_info
96
+ puts @pastel.red("❌ Method '#{method_name}' not found in #{controller[:name]}")
97
+ puts @pastel.yellow("Available methods:")
98
+ methods.first(10).each do |method|
99
+ puts @pastel.dim(" - #{method[:name]}")
100
+ end
101
+ puts @pastel.dim(" ... and #{methods.length - 10} more") if methods.length > 10
102
+ return
103
+ end
104
+
105
+ puts @pastel.bright_white("🎯 Generating test for #{controller[:name]}##{method_info[:name]}...")
106
+
107
+ result = Tng::Services::TestGenerator.new(@http_client).run_for_controller_method(controller, method_info)
108
+
109
+ if result && result[:file_path]
110
+ @show_post_generation_menu.call(result)
111
+ else
112
+ puts @pastel.red("❌ Failed to generate test")
113
+ end
114
+ end
115
+
116
+ def find_matching_controller(controllers, file_name)
117
+ controllers.find do |controller|
118
+ controller[:name].downcase == file_name.downcase ||
119
+ controller[:name].split("::").last.downcase == file_name.downcase ||
120
+ File.basename(controller[:path], ".rb").downcase == file_name.downcase ||
121
+ File.basename(controller[:path], ".rb").gsub("_controller", "").downcase == file_name.downcase ||
122
+ File.basename(controller[:path], ".rb").downcase == "#{file_name}_controller".downcase
123
+ end
124
+ end
125
+
126
+ def run_model_method_generation(file_name, method_name)
127
+ models = Tng::Analyzers::Model.files_in_dir("app/models").map do |file|
128
+ relative_path = file[:path].gsub(%r{^.*app/models/}, "").gsub(".rb", "")
129
+ namespaced_name = relative_path.split("/").map(&:camelize).join("::")
130
+
131
+ {
132
+ name: namespaced_name,
133
+ path: file[:path]
134
+ }
135
+ end
136
+
137
+ model = find_matching_model(models, file_name)
138
+
139
+ unless model
140
+ puts @pastel.red("❌ Model not found: #{file_name}")
141
+ puts @pastel.yellow("Available models:")
142
+ models.first(5).each do |mdl|
143
+ puts @pastel.dim(" - #{mdl[:name]}")
144
+ end
145
+ puts @pastel.dim(" ... and #{models.length - 5} more") if models.length > 5
146
+ return
147
+ end
148
+
149
+ methods = extract_model_methods(model)
150
+
151
+ if methods.empty?
152
+ puts @pastel.yellow("⚠️ No methods found in #{model[:name]}")
153
+ return
154
+ end
155
+
156
+ method_info = methods.find { |method| method[:name].downcase == method_name.downcase }
157
+
158
+ unless method_info
159
+ puts @pastel.red("❌ Method '#{method_name}' not found in #{model[:name]}")
160
+ puts @pastel.yellow("Available methods:")
161
+ methods.first(10).each do |method|
162
+ puts @pastel.dim(" - #{method[:name]}")
163
+ end
164
+ puts @pastel.dim(" ... and #{methods.length - 10} more") if methods.length > 10
165
+ return
166
+ end
167
+
168
+ puts @pastel.bright_white("🎯 Generating test for #{model[:name]}##{method_info[:name]}...")
169
+
170
+ result = Tng::Services::TestGenerator.new(@http_client).run_for_model_method(model, method_info)
171
+
172
+ if result && result[:file_path]
173
+ @show_post_generation_menu.call(result)
174
+ else
175
+ puts @pastel.red("❌ Failed to generate test")
176
+ end
177
+ end
178
+
179
+ def find_matching_model(models, file_name)
180
+ models.find do |model|
181
+ model[:name].downcase == file_name.downcase ||
182
+ model[:name].split("::").last.downcase == file_name.downcase ||
183
+ File.basename(model[:path], ".rb").downcase == file_name.downcase
184
+ end
185
+ end
186
+
187
+ def run_service_method_generation(file_name, method_name)
188
+ services = Tng::Analyzers::Service.files_in_dir.map do |file|
189
+ relative_path = file[:path].gsub(%r{^.*app/services?/}, "").gsub(".rb", "")
190
+ namespaced_name = relative_path.split("/").map(&:camelize).join("::")
191
+
192
+ {
193
+ name: namespaced_name,
194
+ path: file[:path]
195
+ }
196
+ end
197
+
198
+ service = find_matching_service(services, file_name)
199
+
200
+ unless service
201
+ puts @pastel.red("❌ Service not found: #{file_name}")
202
+ puts @pastel.yellow("Available services:")
203
+ services.first(5).each do |svc|
204
+ puts @pastel.dim(" - #{svc[:name]}")
205
+ end
206
+ puts @pastel.dim(" ... and #{services.length - 5} more") if services.length > 5
207
+ return
208
+ end
209
+
210
+ methods = extract_service_methods(service)
211
+
212
+ if methods.empty?
213
+ puts @pastel.yellow("⚠️ No methods found in #{service[:name]}")
214
+ return
215
+ end
216
+
217
+ method_info = methods.find { |method| method[:name].downcase == method_name.downcase }
218
+
219
+ unless method_info
220
+ puts @pastel.red("❌ Method '#{method_name}' not found in #{service[:name]}")
221
+ puts @pastel.yellow("Available methods:")
222
+ methods.first(10).each do |method|
223
+ puts @pastel.dim(" - #{method[:name]}")
224
+ end
225
+ puts @pastel.dim(" ... and #{methods.length - 10} more") if methods.length > 10
226
+ return
227
+ end
228
+
229
+ puts @pastel.bright_white("🎯 Generating test for #{service[:name]}##{method_info[:name]}...")
230
+
231
+ result = Tng::Services::TestGenerator.new(@http_client).run_for_service_method(service, method_info)
232
+
233
+ if result && result[:file_path]
234
+ @show_post_generation_menu.call(result)
235
+ else
236
+ puts @pastel.red("❌ Failed to generate test")
237
+ end
238
+ end
239
+
240
+ def find_matching_service(services, file_name)
241
+ services.find do |service|
242
+ service[:name].downcase == file_name.downcase ||
243
+ service[:name].split("::").last.downcase == file_name.downcase ||
244
+ File.basename(service[:path], ".rb").downcase == file_name.downcase
245
+ end
246
+ end
247
+
248
+ def run_other_method_generation(file_name, method_name)
249
+ other_files = Tng::Analyzers::Other.files_in_dir.map do |file|
250
+ relative_path = file[:relative_path].gsub(".rb", "")
251
+ namespaced_name = relative_path.split("/").map(&:camelize).join("::")
252
+
253
+ {
254
+ name: namespaced_name,
255
+ path: file[:path],
256
+ type: file[:type]
257
+ }
258
+ end
259
+
260
+ other_file = find_matching_other_file(other_files, file_name)
261
+
262
+ unless other_file
263
+ puts @pastel.red("❌ Other file not found: #{file_name}")
264
+ puts @pastel.yellow("Available other files:")
265
+ other_files.first(5).each do |file|
266
+ puts @pastel.dim(" - #{file[:name]}")
267
+ end
268
+ puts @pastel.dim(" ... and #{other_files.length - 5} more") if other_files.length > 5
269
+ return
270
+ end
271
+
272
+ methods = extract_other_methods(other_file)
273
+
274
+ if methods.empty?
275
+ puts @pastel.yellow("⚠️ No methods found in #{other_file[:name]}")
276
+ return
277
+ end
278
+
279
+ method_info = methods.find { |method| method[:name].downcase == method_name.downcase }
280
+
281
+ unless method_info
282
+ puts @pastel.red("❌ Method '#{method_name}' not found in #{other_file[:name]}")
283
+ puts @pastel.yellow("Available methods:")
284
+ methods.first(10).each do |method|
285
+ puts @pastel.dim(" - #{method[:name]}")
286
+ end
287
+ puts @pastel.dim(" ... and #{methods.length - 10} more") if methods.length > 10
288
+ return
289
+ end
290
+
291
+ puts @pastel.bright_white("🎯 Generating test for #{other_file[:name]}##{method_info[:name]}...")
292
+
293
+ result = Tng::Services::TestGenerator.new(@http_client).run_for_other_method(other_file, method_info)
294
+
295
+ if result && result[:file_path]
296
+ @show_post_generation_menu.call(result)
297
+ else
298
+ puts @pastel.red("❌ Failed to generate test")
299
+ end
300
+ end
301
+
302
+ def find_matching_other_file(other_files, file_name)
303
+ other_files.find do |file|
304
+ file[:name].downcase == file_name.downcase ||
305
+ file[:name].split("::").last.downcase == file_name.downcase ||
306
+ File.basename(file[:path], ".rb").downcase == file_name.downcase ||
307
+ normalize_other_file_path(file[:path]).downcase == file_name.downcase
308
+ end
309
+ end
310
+
311
+ def normalize_other_file_path(file_path)
312
+ Tng::Analyzers::Other::OTHER_DIRECTORIES.each do |dir|
313
+ pattern = %r{^.*/#{Regexp.escape(dir)}/}
314
+ return file_path.gsub(pattern, "").gsub(".rb", "") if file_path.match?(pattern)
315
+ end
316
+ file_path # fallback if no match
317
+ end
318
+ end
319
+ end
320
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tng
4
+ module Services
5
+ module ExtractMethods
6
+ def extract_controller_methods(controller)
7
+ methods = Tng::Analyzers::Controller.methods_for_controller(controller[:name])
8
+ Array(methods)
9
+ rescue StandardError => e
10
+ puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing controller: #{e.message}", Tng::UI::Theme.color(:error)))
11
+ []
12
+ end
13
+
14
+ def extract_model_methods(model)
15
+ methods = Tng::Analyzers::Model.methods_for_model(model[:name])
16
+ Array(methods)
17
+ rescue StandardError => e
18
+ puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing model: #{e.message}", Tng::UI::Theme.color(:error)))
19
+ []
20
+ end
21
+
22
+ def extract_service_methods(service)
23
+ methods = Tng::Analyzers::Service.methods_for_service(service[:name])
24
+ Array(methods)
25
+ rescue StandardError => e
26
+ puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing service: #{e.message}", Tng::UI::Theme.color(:error)))
27
+ []
28
+ end
29
+
30
+ def extract_other_methods(other_file)
31
+ methods = Tng::Analyzers::Other.methods_for_other(other_file[:name], other_file[:path])
32
+ Array(methods)
33
+ rescue StandardError => e
34
+ puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing file: #{e.message}", Tng::UI::Theme.color(:error)))
35
+ []
36
+ end
37
+ end
38
+ end
39
+ end