tng 0.1.6 → 0.2.1

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.
data/binaries/tng.bundle CHANGED
Binary file
data/binaries/tng.so CHANGED
Binary file
@@ -106,6 +106,15 @@ module Tng
106
106
  when Prism::DefNode
107
107
  # Both instance and class methods (def self.method_name)
108
108
  methods << node.name.to_s
109
+ when Prism::CallNode
110
+ # Handle scope definitions: scope :name, -> { ... }
111
+ if node.name == :scope && node.arguments&.arguments&.any?
112
+ first_arg = node.arguments.arguments.first
113
+ if first_arg.is_a?(Prism::SymbolNode)
114
+ scope_name = first_arg.value
115
+ methods << scope_name if scope_name
116
+ end
117
+ end
109
118
  when Prism::SingletonClassNode
110
119
  # Methods inside class << self blocks
111
120
  node.body&.child_nodes&.each do |child|
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tng
4
+ module Analyzers
5
+ class Other
6
+ # Directories to scan for "other" files
7
+ OTHER_DIRECTORIES = %w[
8
+ app/jobs
9
+ app/helpers
10
+ lib
11
+ libs
12
+ app/lib
13
+ app/libs
14
+ app/mailers
15
+ app/channels
16
+ app/decorators
17
+ app/presenters
18
+ app/serializers
19
+ app/policies
20
+ app/forms
21
+ app/queries
22
+ app/graphql
23
+ app/graphql/resolvers
24
+ app/graphql/types
25
+ app/graphql/mutations
26
+ app/graphql/loaders
27
+ app/graphql/schemas
28
+ ].freeze
29
+
30
+ def self.files_in_dir(dir = nil)
31
+ base_dirs = if dir.nil?
32
+ OTHER_DIRECTORIES.select { |d| Dir.exist?(File.join(Dir.pwd, d)) }
33
+ else
34
+ [dir]
35
+ end
36
+
37
+ return [] if base_dirs.empty?
38
+
39
+ base_dirs.flat_map do |base_dir|
40
+ full_path = File.join(Dir.pwd, base_dir)
41
+ find_other_files(full_path, base_dir)
42
+ end
43
+ end
44
+
45
+ def self.value_for_other(file_path)
46
+ raise "file_path is required" if file_path.nil?
47
+
48
+ # For now, use the same parsing logic as services
49
+ # We can enhance this later if needed for specific file types
50
+ Tng::Analyzer::Service.parse_service_file(file_path)
51
+ end
52
+
53
+ def self.read_test_file_for_other(other_path)
54
+ files = find_test_file_for_other(other_path)
55
+
56
+ files.map do |file_path|
57
+ {
58
+ path: file_path,
59
+ content: File.read(file_path)
60
+ }
61
+ end
62
+ end
63
+
64
+ def self.find_test_file_for_other(other_path)
65
+ # Extract the file type and name for test file discovery
66
+ file_type = determine_file_type(other_path)
67
+ file_name = File.basename(other_path, ".rb")
68
+ testing_framework = Tng.testing_framework
69
+ return [] if testing_framework.nil?
70
+
71
+ paths = if testing_framework.downcase == "rspec"
72
+ build_rspec_test_paths(file_type, file_name)
73
+ else
74
+ build_minitest_test_paths(file_type, file_name)
75
+ end
76
+
77
+ paths.select { |path| File.exist?(path) }
78
+ end
79
+
80
+ def self.methods_for_other(other_name, file_path)
81
+ raise "other_name is required" if other_name.nil?
82
+ raise "file_path is required" if file_path.nil?
83
+
84
+ begin
85
+ # Try to load the class/module if possible
86
+ if File.exist?(file_path)
87
+ source_code = File.read(file_path)
88
+ result = Prism.parse(source_code)
89
+
90
+ public_methods = []
91
+ extract_public_method_names(result.value, public_methods, :public)
92
+
93
+ # Return the public methods found in the source file
94
+ public_methods.map { |method_name| { name: method_name.to_s } }
95
+ else
96
+ []
97
+ end
98
+ rescue StandardError => e
99
+ puts "❌ Error analyzing file #{other_name}: #{e.message}"
100
+ []
101
+ end
102
+ end
103
+
104
+ def self.extract_public_method_names(node, methods, current_visibility = :public)
105
+ return unless node.is_a?(Prism::Node)
106
+
107
+ case node
108
+ when Prism::DefNode
109
+ # Only add methods that are public
110
+ methods << node.name.to_s if current_visibility == :public
111
+ when Prism::CallNode
112
+ # Handle visibility modifiers (private, protected, public)
113
+ if node.receiver.nil? && node.arguments.nil?
114
+ case node.name
115
+ when :private
116
+ current_visibility = :private
117
+ when :protected
118
+ current_visibility = :protected
119
+ when :public
120
+ current_visibility = :public
121
+ end
122
+
123
+ end
124
+ when Prism::SingletonClassNode
125
+ # Methods inside class << self blocks - always public by default
126
+ node.body&.child_nodes&.each do |child|
127
+ extract_public_method_names(child, methods, :public)
128
+ end
129
+ end
130
+
131
+ # Recursively process child nodes with current visibility
132
+ node.child_nodes.each do |child|
133
+ extract_public_method_names(child, methods, current_visibility)
134
+ end
135
+ end
136
+
137
+ def self.find_other_files(dir, _base_dir)
138
+ return [] unless Dir.exist?(dir)
139
+
140
+ Dir.glob(File.join(dir, "**", "*.rb")).map do |file_path|
141
+ relative_path = file_path.gsub("#{Dir.pwd}/", "")
142
+ file_type = determine_file_type(file_path)
143
+
144
+ {
145
+ file: File.basename(file_path),
146
+ path: file_path,
147
+ relative_path: relative_path,
148
+ type: file_type
149
+ }
150
+ end
151
+ end
152
+
153
+ def self.determine_file_type(file_path)
154
+ case file_path
155
+ when %r{app/jobs}
156
+ "job"
157
+ when %r{app/helpers}
158
+ "helper"
159
+ when %r{(?:app/)?libs?(?:/|$)}
160
+ "lib"
161
+ when %r{app/mailers}
162
+ "mailer"
163
+ when %r{app/channels}
164
+ "channel"
165
+ when %r{app/decorators}
166
+ "decorator"
167
+ when %r{app/presenters}
168
+ "presenter"
169
+ when %r{app/serializers}
170
+ "serializer"
171
+ when %r{app/policies}
172
+ "policy"
173
+ when %r{app/forms}
174
+ "form"
175
+ when %r{app/queries}
176
+ "query"
177
+ when %r{app/graphql/resolvers}
178
+ "resolver"
179
+ when %r{app/graphql/types}
180
+ "type"
181
+ when %r{app/graphql/mutations}
182
+ "mutation"
183
+ when %r{app/graphql/loaders}
184
+ "loader"
185
+ when %r{app/graphql/schemas}
186
+ "schema"
187
+ when %r{app/graphql}
188
+ "graphql"
189
+ else
190
+ "other"
191
+ end
192
+ end
193
+
194
+ def self.build_rspec_test_paths(file_type, file_name)
195
+ case file_type
196
+ when "job"
197
+ ["spec/jobs/#{file_name}_spec.rb"]
198
+ when "helper"
199
+ ["spec/helpers/#{file_name}_spec.rb"]
200
+ when "lib"
201
+ ["spec/lib/#{file_name}_spec.rb"]
202
+ when "mailer"
203
+ ["spec/mailers/#{file_name}_spec.rb"]
204
+ when "channel"
205
+ ["spec/channels/#{file_name}_spec.rb"]
206
+ when "query"
207
+ ["spec/queries/#{file_name}_spec.rb"]
208
+ when "decorator"
209
+ ["spec/decorators/#{file_name}_spec.rb"]
210
+ when "presenter"
211
+ ["spec/presenters/#{file_name}_spec.rb"]
212
+ when "serializer"
213
+ ["spec/serializers/#{file_name}_spec.rb"]
214
+ when "policy"
215
+ ["spec/policies/#{file_name}_spec.rb"]
216
+ when "form"
217
+ ["spec/forms/#{file_name}_spec.rb"]
218
+ when "resolver"
219
+ ["spec/graphql/resolvers/#{file_name}_spec.rb"]
220
+ when "type"
221
+ ["spec/graphql/types/#{file_name}_spec.rb"]
222
+ when "mutation"
223
+ ["spec/graphql/mutations/#{file_name}_spec.rb"]
224
+ when "loader"
225
+ ["spec/graphql/loaders/#{file_name}_spec.rb"]
226
+ when "schema"
227
+ ["spec/graphql/schemas/#{file_name}_spec.rb"]
228
+ when "graphql"
229
+ ["spec/graphql/#{file_name}_spec.rb"]
230
+ else
231
+ ["spec/#{file_type}s/#{file_name}_spec.rb"]
232
+ end
233
+ end
234
+
235
+ def self.build_minitest_test_paths(file_type, file_name)
236
+ case file_type
237
+ when "job"
238
+ ["test/jobs/#{file_name}_test.rb"]
239
+ when "helper"
240
+ ["test/helpers/#{file_name}_test.rb"]
241
+ when "lib"
242
+ ["test/lib/#{file_name}_test.rb"]
243
+ when "mailer"
244
+ ["test/mailers/#{file_name}_test.rb"]
245
+ when "channel"
246
+ ["test/channels/#{file_name}_test.rb"]
247
+ when "query"
248
+ ["test/queries/#{file_name}_test.rb"]
249
+ when "decorator"
250
+ ["test/decorators/#{file_name}_test.rb"]
251
+ when "presenter"
252
+ ["test/presenters/#{file_name}_test.rb"]
253
+ when "serializer"
254
+ ["test/serializers/#{file_name}_test.rb"]
255
+ when "policy"
256
+ ["test/policies/#{file_name}_test.rb"]
257
+ when "form"
258
+ ["test/forms/#{file_name}_test.rb"]
259
+ when "resolver"
260
+ ["test/graphql/resolvers/#{file_name}_test.rb"]
261
+ when "type"
262
+ ["test/graphql/types/#{file_name}_test.rb"]
263
+ when "mutation"
264
+ ["test/graphql/mutations/#{file_name}_test.rb"]
265
+ when "loader"
266
+ ["test/graphql/loaders/#{file_name}_test.rb"]
267
+ when "schema"
268
+ ["test/graphql/schemas/#{file_name}_test.rb"]
269
+ when "graphql"
270
+ ["test/graphql/#{file_name}_test.rb"]
271
+ else
272
+ ["test/#{file_type}s/#{file_name}_test.rb"]
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
@@ -120,6 +120,7 @@ module Tng
120
120
  when Prism::DefNode
121
121
  # Both instance and class methods (def self.method_name)
122
122
  methods << node.name.to_s
123
+
123
124
  when Prism::SingletonClassNode
124
125
  # Methods inside class << self blocks
125
126
  node.body&.child_nodes&.each do |child|
@@ -14,13 +14,7 @@ module Tng
14
14
  end
15
15
 
16
16
  def post(path, payload: {}, headers: {})
17
- default_headers = {
18
- "Content-Type" => "application/json",
19
- "Authorization" => "Bearer #{@api_key}",
20
- "User-Agent" => "TestNG-Rails/#{Tng::VERSION} Ruby/#{RUBY_VERSION}"
21
- }
22
-
23
- merged_headers = default_headers.merge(headers)
17
+ merged_headers = json_default_headers.merge(headers)
24
18
 
25
19
  response = HTTPX.with(timeout: @timeout).post(
26
20
  "#{@api_endpoint}/#{path}",
@@ -33,13 +27,7 @@ module Tng
33
27
  end
34
28
 
35
29
  def post_binary(path, data, headers: {})
36
- default_headers = {
37
- "Content-Type" => "application/octet-stream",
38
- "Authorization" => "Bearer #{@api_key}",
39
- "User-Agent" => "TestNG-Rails/#{Tng::VERSION} Ruby/#{RUBY_VERSION}"
40
- }
41
-
42
- merged_headers = default_headers.merge(headers)
30
+ merged_headers = stream_default_headers.merge(headers)
43
31
 
44
32
  response = HTTPX.with(timeout: @timeout).post(
45
33
  "#{@api_endpoint}/#{path}",
@@ -52,13 +40,7 @@ module Tng
52
40
  end
53
41
 
54
42
  def get(path, headers: {})
55
- default_headers = {
56
- "Content-Type" => "application/json",
57
- "Authorization" => "Bearer #{@api_key}",
58
- "User-Agent" => "TestNG-Rails/#{Tng::VERSION} Ruby/#{RUBY_VERSION}"
59
- }
60
-
61
- merged_headers = default_headers.merge(headers)
43
+ merged_headers = json_default_headers.merge(headers)
62
44
 
63
45
  response = HTTPX.with(timeout: @timeout).get(
64
46
  "#{@api_endpoint}/#{path}",
@@ -77,18 +59,34 @@ module Tng
77
59
 
78
60
  private
79
61
 
62
+ def stream_default_headers
63
+ {
64
+ "Content-Type" => "application/octet-stream",
65
+ "Authorization" => "Bearer #{@api_key}",
66
+ "User-Agent" => "TestNG-Rails/#{Tng::VERSION} Ruby/#{RUBY_VERSION}"
67
+ }
68
+ end
69
+
70
+ def json_default_headers
71
+ {
72
+ "Content-Type" => "application/json",
73
+ "Authorization" => "Bearer #{@api_key}",
74
+ "User-Agent" => "TestNG-Rails/#{Tng::VERSION} Ruby/#{RUBY_VERSION}"
75
+ }
76
+ end
77
+
80
78
  def debug_enabled?
81
79
  ENV["DEBUG"] == "1"
82
80
  end
83
81
 
84
82
  def debug_response(request_info, response)
85
83
  puts "\n -> DEBUG: #{request_info}"
86
- puts " Status: #{response.status}"
87
- puts " Headers: #{response.headers.to_h}"
88
84
 
89
85
  if response.is_a?(HTTPX::ErrorResponse)
90
86
  puts " Error: #{response.error&.message}"
91
87
  else
88
+ puts " Status: #{response.status}"
89
+ puts " Headers: #{response.headers.to_h}"
92
90
  body = response.body.to_s
93
91
  if body.length > 500
94
92
  puts " Body: #{body[0..500]}... (truncated, total length: #{body.length})"