tng 0.2.3 → 0.2.4
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 +4 -4
- data/README.md +29 -2
- data/Rakefile +41 -7
- data/bin/tng +155 -51
- data/lib/tng/analyzers/controller.rb +0 -31
- data/lib/tng/analyzers/model.rb +0 -31
- data/lib/tng/analyzers/other.rb +0 -27
- data/lib/tng/analyzers/service.rb +0 -31
- data/lib/tng/services/direct_generation.rb +52 -250
- data/lib/tng/services/extract_methods.rb +4 -8
- data/lib/tng/services/file_type_detector.rb +124 -0
- data/lib/tng/services/test_generator.rb +83 -163
- data/lib/tng/ui/authentication_warning_display.rb +0 -50
- data/lib/tng/ui/theme.rb +2 -1
- data/lib/tng/utils.rb +139 -0
- data/lib/tng/version.rb +1 -1
- metadata +3 -2
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "extract_methods"
|
4
|
+
require_relative "file_type_detector"
|
4
5
|
|
5
6
|
module Tng
|
6
7
|
module Services
|
@@ -16,281 +17,79 @@ module Tng
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def run
|
19
|
-
|
20
|
-
file_name = @params[:file]
|
21
|
-
method_name = @params[:method]
|
20
|
+
file_path, method_name = @params[:file], @params[:method]
|
22
21
|
|
23
|
-
unless
|
24
|
-
puts @pastel.red("❌
|
25
|
-
puts @pastel.yellow("Usage: bundle exec tng
|
26
|
-
puts @pastel.yellow("
|
22
|
+
unless file_path && method_name
|
23
|
+
puts @pastel.red("❌ Both file and method parameters are required")
|
24
|
+
puts @pastel.yellow("Usage: bundle exec tng app/controllers/users_controller.rb index")
|
25
|
+
puts @pastel.yellow(" or: bundle exec tng --file=users_controller.rb --method=index")
|
26
|
+
puts @pastel.yellow(" or: bundle exec tng f=users_controller.rb m=index")
|
27
27
|
return
|
28
28
|
end
|
29
29
|
|
30
|
-
|
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
|
30
|
+
resolved_path = FileTypeDetector.resolve_file_path(file_path)
|
73
31
|
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
32
|
+
unless resolved_path && File.exist?(resolved_path)
|
33
|
+
puts @pastel.red("❌ File not found: #{file_path}")
|
34
|
+
suggest_similar_files(file_path)
|
102
35
|
return
|
103
36
|
end
|
104
37
|
|
105
|
-
|
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
|
38
|
+
type = FileTypeDetector.detect_type(resolved_path)
|
39
|
+
generate_test_for_file(resolved_path, method_name, type)
|
124
40
|
end
|
125
41
|
|
126
|
-
|
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("::")
|
42
|
+
private
|
130
43
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
}
|
135
|
-
end
|
44
|
+
def suggest_similar_files(file_path)
|
45
|
+
base_name = File.basename(file_path, '.rb')
|
46
|
+
puts @pastel.yellow("💡 Did you mean one of these?")
|
136
47
|
|
137
|
-
|
48
|
+
similar_files = find_similar_files(base_name)
|
138
49
|
|
139
|
-
|
140
|
-
puts @pastel.
|
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)
|
50
|
+
if similar_files.empty?
|
51
|
+
puts @pastel.dim(" No similar files found")
|
174
52
|
else
|
175
|
-
puts @pastel.
|
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
|
53
|
+
similar_files.first(5).each { |file| puts @pastel.dim(" • #{file}") }
|
184
54
|
end
|
185
55
|
end
|
186
56
|
|
187
|
-
def
|
188
|
-
|
189
|
-
|
190
|
-
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
57
|
+
def find_similar_files(base_name)
|
58
|
+
rails_root = defined?(Rails) && Rails.root ? Rails.root.to_s : Dir.pwd
|
59
|
+
similar_files = []
|
191
60
|
|
192
|
-
|
193
|
-
|
194
|
-
path: file[:path]
|
195
|
-
}
|
196
|
-
end
|
61
|
+
%w[app/controllers app/models app/services app/service].each do |dir|
|
62
|
+
next unless Dir.exist?(File.join(rails_root, dir))
|
197
63
|
|
198
|
-
|
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]}")
|
64
|
+
Dir.glob(File.join(rails_root, dir, '**', "*#{base_name}*.rb")).each do |file|
|
65
|
+
similar_files << file.gsub(/^#{Regexp.escape(rails_root)}\//, '')
|
205
66
|
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
67
|
end
|
216
68
|
|
217
|
-
|
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
|
69
|
+
similar_files
|
246
70
|
end
|
247
71
|
|
248
|
-
def
|
249
|
-
|
250
|
-
|
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)
|
72
|
+
def generate_test_for_file(file_path, method_name, type)
|
73
|
+
file_object = FileTypeDetector.build_file_object(file_path, type)
|
74
|
+
methods = extract_methods_for_type(file_object, type)
|
273
75
|
|
274
76
|
if methods.empty?
|
275
|
-
puts @pastel.yellow("⚠️ No methods found in #{
|
77
|
+
puts @pastel.yellow("⚠️ No methods found in #{file_object[:name]}")
|
276
78
|
return
|
277
79
|
end
|
278
80
|
|
279
|
-
method_info = methods.find { |
|
81
|
+
method_info = methods.find { |m| m[:name].downcase == method_name.downcase }
|
280
82
|
|
281
83
|
unless method_info
|
282
|
-
puts @pastel.red("❌ Method '#{method_name}' not found in #{
|
84
|
+
puts @pastel.red("❌ Method '#{method_name}' not found in #{file_object[:name]}")
|
283
85
|
puts @pastel.yellow("Available methods:")
|
284
|
-
methods.first(10).each
|
285
|
-
puts @pastel.dim(" - #{method[:name]}")
|
286
|
-
end
|
287
|
-
puts @pastel.dim(" ... and #{methods.length - 10} more") if methods.length > 10
|
86
|
+
methods.first(10).each { |m| puts @pastel.dim(" • #{m[:name]}") }
|
288
87
|
return
|
289
88
|
end
|
290
89
|
|
291
|
-
puts @pastel.bright_white("🎯 Generating test for #{
|
90
|
+
puts @pastel.bright_white("🎯 Generating test for #{file_object[:name]}##{method_info[:name]}...")
|
292
91
|
|
293
|
-
result =
|
92
|
+
result = generate_test_result(file_object, method_info, type)
|
294
93
|
|
295
94
|
if result && result[:file_path]
|
296
95
|
@show_post_generation_menu.call(result)
|
@@ -299,21 +98,24 @@ module Tng
|
|
299
98
|
end
|
300
99
|
end
|
301
100
|
|
302
|
-
def
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
101
|
+
def extract_methods_for_type(file_object, type)
|
102
|
+
case type
|
103
|
+
when "controller" then extract_controller_methods(file_object)
|
104
|
+
when "model" then extract_model_methods(file_object)
|
105
|
+
when "service" then extract_service_methods(file_object)
|
106
|
+
else extract_other_methods(file_object)
|
308
107
|
end
|
309
108
|
end
|
310
109
|
|
311
|
-
def
|
312
|
-
Tng::
|
313
|
-
|
314
|
-
|
110
|
+
def generate_test_result(file_object, method_info, type)
|
111
|
+
generator = Tng::Services::TestGenerator.new(@http_client)
|
112
|
+
|
113
|
+
case type
|
114
|
+
when "controller" then generator.run_for_controller_method(file_object, method_info)
|
115
|
+
when "model" then generator.run_for_model_method(file_object, method_info)
|
116
|
+
when "service" then generator.run_for_service_method(file_object, method_info)
|
117
|
+
else generator.run_for_other_method(file_object, method_info)
|
315
118
|
end
|
316
|
-
file_path # fallback if no match
|
317
119
|
end
|
318
120
|
end
|
319
121
|
end
|
@@ -4,32 +4,28 @@ module Tng
|
|
4
4
|
module Services
|
5
5
|
module ExtractMethods
|
6
6
|
def extract_controller_methods(controller)
|
7
|
-
|
8
|
-
Array(methods)
|
7
|
+
Tng::Analyzers::Controller.methods_for_controller(controller[:name]) || []
|
9
8
|
rescue StandardError => e
|
10
9
|
puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing controller: #{e.message}", Tng::UI::Theme.color(:error)))
|
11
10
|
[]
|
12
11
|
end
|
13
12
|
|
14
13
|
def extract_model_methods(model)
|
15
|
-
|
16
|
-
Array(methods)
|
14
|
+
Tng::Analyzers::Model.methods_for_model(model[:name]) || []
|
17
15
|
rescue StandardError => e
|
18
16
|
puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing model: #{e.message}", Tng::UI::Theme.color(:error)))
|
19
17
|
[]
|
20
18
|
end
|
21
19
|
|
22
20
|
def extract_service_methods(service)
|
23
|
-
|
24
|
-
Array(methods)
|
21
|
+
Tng::Analyzers::Service.methods_for_service(service[:name]) || []
|
25
22
|
rescue StandardError => e
|
26
23
|
puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing service: #{e.message}", Tng::UI::Theme.color(:error)))
|
27
24
|
[]
|
28
25
|
end
|
29
26
|
|
30
27
|
def extract_other_methods(other_file)
|
31
|
-
|
32
|
-
Array(methods)
|
28
|
+
Tng::Analyzers::Other.methods_for_other(other_file[:name], other_file[:path]) || []
|
33
29
|
rescue StandardError => e
|
34
30
|
puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error analyzing file: #{e.message}", Tng::UI::Theme.color(:error)))
|
35
31
|
[]
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/string/inflections'
|
4
|
+
|
5
|
+
module Tng
|
6
|
+
module Services
|
7
|
+
class FileTypeDetector
|
8
|
+
class << self
|
9
|
+
TYPE_PATTERNS = {
|
10
|
+
/app\/controllers\// => 'controller',
|
11
|
+
/app\/models\// => 'model',
|
12
|
+
/app\/services?\// => 'service',
|
13
|
+
/app\/jobs\// => 'job',
|
14
|
+
/app\/helpers\// => 'helper',
|
15
|
+
/(?:app\/)?libs?\// => 'lib',
|
16
|
+
/app\/mailers\// => 'mailer',
|
17
|
+
/app\/channels\// => 'channel',
|
18
|
+
/app\/decorators\// => 'decorator',
|
19
|
+
/app\/presenters\// => 'presenter',
|
20
|
+
/app\/serializers\// => 'serializer',
|
21
|
+
/app\/policies\// => 'policy',
|
22
|
+
/app\/forms\// => 'form',
|
23
|
+
/app\/queries\// => 'query',
|
24
|
+
/app\/graphql\/resolvers\// => 'resolver',
|
25
|
+
/app\/graphql\/types\// => 'type',
|
26
|
+
/app\/graphql\/mutations\// => 'mutation',
|
27
|
+
/app\/graphql\/loaders\// => 'loader',
|
28
|
+
/app\/graphql\/schemas\// => 'schema',
|
29
|
+
/app\/graphql\// => 'graphql'
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
PATH_MAPPINGS = {
|
33
|
+
'controller' => 'app/controllers',
|
34
|
+
'model' => 'app/models',
|
35
|
+
'service' => 'app/services?',
|
36
|
+
'job' => 'app/jobs',
|
37
|
+
'helper' => 'app/helpers',
|
38
|
+
'lib' => 'lib',
|
39
|
+
'mailer' => 'app/mailers',
|
40
|
+
'channel' => 'app/channels',
|
41
|
+
'decorator' => 'app/decorators',
|
42
|
+
'presenter' => 'app/presenters',
|
43
|
+
'serializer' => 'app/serializers',
|
44
|
+
'policy' => 'app/policies',
|
45
|
+
'form' => 'app/forms',
|
46
|
+
'query' => 'app/queries',
|
47
|
+
'resolver' => 'app/graphql/resolvers',
|
48
|
+
'type' => 'app/graphql/types',
|
49
|
+
'mutation' => 'app/graphql/mutations',
|
50
|
+
'loader' => 'app/graphql/loaders',
|
51
|
+
'schema' => 'app/graphql/schemas',
|
52
|
+
'graphql' => 'app/graphql'
|
53
|
+
}.freeze
|
54
|
+
|
55
|
+
def detect_type(file_path)
|
56
|
+
normalized_path = normalize_path(file_path)
|
57
|
+
TYPE_PATTERNS.find { |pattern, _| normalized_path.match?(pattern) }&.last || 'other'
|
58
|
+
end
|
59
|
+
|
60
|
+
def normalize_path(file_path)
|
61
|
+
path = file_path.sub(/\.rb$/, '')
|
62
|
+
path.start_with?('/') ? path : (find_file_in_project(path) || path)
|
63
|
+
end
|
64
|
+
|
65
|
+
SEARCH_PATHS = %w[
|
66
|
+
app/controllers app/models app/services app/service
|
67
|
+
app/jobs app/helpers app/mailers app/channels
|
68
|
+
app/decorators app/presenters app/serializers
|
69
|
+
app/policies app/forms app/queries app/graphql
|
70
|
+
lib app/lib
|
71
|
+
].freeze
|
72
|
+
|
73
|
+
def find_file_in_project(file_name)
|
74
|
+
file_with_ext = file_name.end_with?('.rb') ? file_name : "#{file_name}.rb"
|
75
|
+
|
76
|
+
return File.expand_path(file_with_ext) if File.exist?(file_with_ext)
|
77
|
+
|
78
|
+
rails_root = defined?(Rails) && Rails.root ? Rails.root.to_s : Dir.pwd
|
79
|
+
|
80
|
+
SEARCH_PATHS.each do |dir|
|
81
|
+
full_path = File.join(rails_root, dir, file_with_ext)
|
82
|
+
return full_path if File.exist?(full_path)
|
83
|
+
|
84
|
+
found_files = Dir.glob(File.join(rails_root, dir, '**', file_with_ext))
|
85
|
+
return found_files.first unless found_files.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def resolve_file_path(file_path)
|
92
|
+
resolved = if file_path.start_with?('/')
|
93
|
+
File.exist?(file_path) ? file_path : File.exist?("#{file_path}.rb") ? "#{file_path}.rb" : nil
|
94
|
+
else
|
95
|
+
found = find_file_in_project(file_path)
|
96
|
+
found ? File.expand_path(found) : nil
|
97
|
+
end
|
98
|
+
|
99
|
+
resolved
|
100
|
+
end
|
101
|
+
|
102
|
+
def extract_relative_path(file_path, type)
|
103
|
+
base_path = PATH_MAPPINGS[type]
|
104
|
+
return File.basename(file_path, '.rb') unless base_path
|
105
|
+
|
106
|
+
pattern = base_path == 'lib' ? %r{^.*/#{base_path}/} : %r{^.*#{base_path}/}
|
107
|
+
file_path.gsub(pattern, '').gsub('.rb', '')
|
108
|
+
end
|
109
|
+
|
110
|
+
def build_file_object(file_path, type)
|
111
|
+
actual_file_path = File.expand_path(file_path)
|
112
|
+
relative_path = extract_relative_path(actual_file_path, type)
|
113
|
+
namespaced_name = relative_path.split('/').map(&:camelize).join('::')
|
114
|
+
|
115
|
+
{
|
116
|
+
name: namespaced_name,
|
117
|
+
path: actual_file_path,
|
118
|
+
type: type
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|