QueryWise 0.2.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 (90) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +45 -0
  3. data/CLOUD_RUN_README.md +263 -0
  4. data/DOCKER_README.md +327 -0
  5. data/Dockerfile +69 -0
  6. data/Dockerfile.cloudrun +76 -0
  7. data/Dockerfile.dev +36 -0
  8. data/GEM_Gemfile +16 -0
  9. data/GEM_README.md +421 -0
  10. data/GEM_Rakefile +10 -0
  11. data/GEM_gitignore +137 -0
  12. data/LICENSE.txt +21 -0
  13. data/PUBLISHING_GUIDE.md +269 -0
  14. data/README.md +392 -0
  15. data/app/controllers/api/v1/analysis_controller.rb +340 -0
  16. data/app/controllers/api/v1/api_keys_controller.rb +83 -0
  17. data/app/controllers/api/v1/base_controller.rb +93 -0
  18. data/app/controllers/api/v1/health_controller.rb +86 -0
  19. data/app/controllers/application_controller.rb +2 -0
  20. data/app/controllers/concerns/.keep +0 -0
  21. data/app/jobs/application_job.rb +7 -0
  22. data/app/mailers/application_mailer.rb +4 -0
  23. data/app/models/app_profile.rb +18 -0
  24. data/app/models/application_record.rb +3 -0
  25. data/app/models/concerns/.keep +0 -0
  26. data/app/models/optimization_suggestion.rb +44 -0
  27. data/app/models/query_analysis.rb +47 -0
  28. data/app/models/query_pattern.rb +55 -0
  29. data/app/services/missing_index_detector_service.rb +244 -0
  30. data/app/services/n_plus_one_detector_service.rb +177 -0
  31. data/app/services/slow_query_analyzer_service.rb +225 -0
  32. data/app/services/sql_parser_service.rb +352 -0
  33. data/app/validators/query_data_validator.rb +96 -0
  34. data/app/views/layouts/mailer.html.erb +13 -0
  35. data/app/views/layouts/mailer.text.erb +1 -0
  36. data/app.yaml +109 -0
  37. data/cloudbuild.yaml +47 -0
  38. data/config/application.rb +32 -0
  39. data/config/boot.rb +4 -0
  40. data/config/cable.yml +17 -0
  41. data/config/cache.yml +16 -0
  42. data/config/credentials.yml.enc +1 -0
  43. data/config/database.yml +69 -0
  44. data/config/deploy.yml +116 -0
  45. data/config/environment.rb +5 -0
  46. data/config/environments/development.rb +70 -0
  47. data/config/environments/production.rb +87 -0
  48. data/config/environments/test.rb +53 -0
  49. data/config/initializers/cors.rb +16 -0
  50. data/config/initializers/filter_parameter_logging.rb +8 -0
  51. data/config/initializers/inflections.rb +16 -0
  52. data/config/locales/en.yml +31 -0
  53. data/config/master.key +1 -0
  54. data/config/puma.rb +41 -0
  55. data/config/puma_cloudrun.rb +48 -0
  56. data/config/queue.yml +18 -0
  57. data/config/recurring.yml +15 -0
  58. data/config/routes.rb +28 -0
  59. data/config/storage.yml +34 -0
  60. data/config.ru +6 -0
  61. data/db/cable_schema.rb +11 -0
  62. data/db/cache_schema.rb +14 -0
  63. data/db/migrate/20250818214709_create_app_profiles.rb +13 -0
  64. data/db/migrate/20250818214731_create_query_analyses.rb +22 -0
  65. data/db/migrate/20250818214740_create_query_patterns.rb +22 -0
  66. data/db/migrate/20250818214805_create_optimization_suggestions.rb +20 -0
  67. data/db/queue_schema.rb +129 -0
  68. data/db/schema.rb +79 -0
  69. data/db/seeds.rb +9 -0
  70. data/init.sql +9 -0
  71. data/lib/query_optimizer_client/client.rb +176 -0
  72. data/lib/query_optimizer_client/configuration.rb +43 -0
  73. data/lib/query_optimizer_client/generators/install_generator.rb +43 -0
  74. data/lib/query_optimizer_client/generators/templates/README +46 -0
  75. data/lib/query_optimizer_client/generators/templates/analysis_job.rb +84 -0
  76. data/lib/query_optimizer_client/generators/templates/initializer.rb +30 -0
  77. data/lib/query_optimizer_client/middleware.rb +126 -0
  78. data/lib/query_optimizer_client/railtie.rb +37 -0
  79. data/lib/query_optimizer_client/tasks.rake +228 -0
  80. data/lib/query_optimizer_client/version.rb +5 -0
  81. data/lib/query_optimizer_client.rb +48 -0
  82. data/lib/tasks/.keep +0 -0
  83. data/public/robots.txt +1 -0
  84. data/query_optimizer_client.gemspec +60 -0
  85. data/script/.keep +0 -0
  86. data/storage/.keep +0 -0
  87. data/storage/development.sqlite3 +0 -0
  88. data/storage/test.sqlite3 +0 -0
  89. data/vendor/.keep +0 -0
  90. metadata +265 -0
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :query_optimizer do
4
+ desc "Check configuration and connectivity"
5
+ task check: :environment do
6
+ puts "🔍 Query Optimizer Client Configuration Check"
7
+ puts "=" * 50
8
+
9
+ config = QueryOptimizerClient.configuration
10
+
11
+ puts "API URL: #{config.api_url}"
12
+ puts "API Key: #{config.api_key ? '[SET]' : '[NOT SET]'}"
13
+ puts "Enabled: #{config.enabled?}"
14
+ puts "Timeout: #{config.timeout}s"
15
+ puts "Retries: #{config.retries}"
16
+ puts "Batch Size: #{config.batch_size}"
17
+ puts "Default Threshold: #{config.default_threshold}%"
18
+
19
+ if config.enabled?
20
+ puts "\n🔗 Testing API connectivity..."
21
+
22
+ begin
23
+ result = QueryOptimizerClient.client.health_check
24
+
25
+ if result&.dig('status') == 'ok'
26
+ puts "✅ API connection successful"
27
+ puts " Version: #{result['version']}"
28
+ puts " Services: #{result['services']}"
29
+ else
30
+ puts "⚠️ API responded but status is: #{result&.dig('status')}"
31
+ end
32
+ rescue QueryOptimizerClient::AuthenticationError
33
+ puts "❌ Authentication failed - check your API key"
34
+ rescue QueryOptimizerClient::APIError => e
35
+ puts "❌ API error: #{e.message}"
36
+ rescue => e
37
+ puts "❌ Connection failed: #{e.message}"
38
+ end
39
+ else
40
+ puts "\n⚠️ Client is disabled or not configured"
41
+ end
42
+ end
43
+
44
+ desc "Analyze sample queries"
45
+ task analyze: :environment do
46
+ unless QueryOptimizerClient.enabled?
47
+ puts "❌ Query Optimizer Client is not enabled"
48
+ exit 1
49
+ end
50
+
51
+ puts "🔍 Analyzing sample queries..."
52
+
53
+ # Collect some sample queries from your application
54
+ queries = collect_sample_queries
55
+
56
+ if queries.empty?
57
+ puts "⚠️ No queries to analyze"
58
+ exit 0
59
+ end
60
+
61
+ puts "Collected #{queries.length} queries"
62
+
63
+ begin
64
+ result = QueryOptimizerClient.analyze_queries(queries)
65
+
66
+ if result&.dig('success')
67
+ display_analysis_results(result['data'])
68
+ else
69
+ puts "❌ Analysis failed: #{result&.dig('error')}"
70
+ end
71
+ rescue => e
72
+ puts "❌ Error during analysis: #{e.message}"
73
+ end
74
+ end
75
+
76
+ desc "Run CI performance check"
77
+ task :ci, [:threshold] => :environment do |t, args|
78
+ threshold = args[:threshold]&.to_i || QueryOptimizerClient.configuration.default_threshold
79
+
80
+ unless QueryOptimizerClient.enabled?
81
+ puts "⚠️ Query Optimizer Client is disabled, skipping check"
82
+ exit 0
83
+ end
84
+
85
+ puts "🔍 Running CI performance check (threshold: #{threshold}%)"
86
+
87
+ queries = collect_sample_queries
88
+
89
+ if queries.empty?
90
+ puts "⚠️ No queries to analyze, passing by default"
91
+ exit 0
92
+ end
93
+
94
+ begin
95
+ result = QueryOptimizerClient.analyze_for_ci(queries, threshold: threshold)
96
+
97
+ if result&.dig('data', 'passed')
98
+ puts "✅ Performance check PASSED (Score: #{result['data']['score']}%)"
99
+ exit 0
100
+ else
101
+ puts "❌ Performance check FAILED (Score: #{result['data']['score']}%)"
102
+
103
+ if result['data']['recommendations']
104
+ puts "\n💡 Recommendations:"
105
+ result['data']['recommendations'].each do |rec|
106
+ puts " - #{rec}"
107
+ end
108
+ end
109
+
110
+ exit 1
111
+ end
112
+ rescue => e
113
+ puts "❌ CI check failed: #{e.message}"
114
+ exit 1
115
+ end
116
+ end
117
+
118
+ desc "Generate API key"
119
+ task :generate_key, [:app_name] => :environment do |t, args|
120
+ app_name = args[:app_name] || "Rails App #{Time.current.strftime('%Y%m%d')}"
121
+
122
+ puts "🔑 Generating API key for: #{app_name}"
123
+
124
+ begin
125
+ result = QueryOptimizerClient.client.create_api_key(app_name)
126
+
127
+ if result&.dig('success')
128
+ puts "✅ API key created successfully!"
129
+ puts "App Name: #{result['data']['app_name']}"
130
+ puts "API Key: #{result['data']['api_key']}"
131
+ puts "\nAdd this to your environment:"
132
+ puts "QUERY_OPTIMIZER_API_KEY=#{result['data']['api_key']}"
133
+ else
134
+ puts "❌ Failed to create API key: #{result&.dig('error')}"
135
+ end
136
+ rescue => e
137
+ puts "❌ Error creating API key: #{e.message}"
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def collect_sample_queries
144
+ queries = []
145
+
146
+ # Try to collect some real queries from your models
147
+ if defined?(ActiveRecord::Base)
148
+ subscription = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, start, finish, id, payload|
149
+ duration = (finish - start) * 1000
150
+ next if payload[:name] =~ /SCHEMA|CACHE/ || duration < 1
151
+
152
+ queries << {
153
+ sql: payload[:sql],
154
+ duration_ms: duration.round(2)
155
+ }
156
+ end
157
+
158
+ # Execute some common queries
159
+ begin
160
+ if defined?(User) && User.respond_to?(:limit)
161
+ User.limit(5).to_a
162
+ end
163
+
164
+ if defined?(ApplicationRecord)
165
+ ApplicationRecord.descendants.first(3).each do |model|
166
+ next if model.abstract_class?
167
+ model.limit(3).to_a rescue nil
168
+ end
169
+ end
170
+ rescue => e
171
+ # Ignore errors, we're just trying to collect sample queries
172
+ ensure
173
+ ActiveSupport::Notifications.unsubscribe(subscription) if subscription
174
+ end
175
+ end
176
+
177
+ # Add some default queries if we couldn't collect any
178
+ if queries.empty?
179
+ queries = [
180
+ { sql: "SELECT * FROM users WHERE active = true", duration_ms: 45 },
181
+ { sql: "SELECT * FROM posts WHERE created_at > '2024-01-01'", duration_ms: 120 }
182
+ ]
183
+ end
184
+
185
+ queries
186
+ end
187
+
188
+ def display_analysis_results(data)
189
+ puts "\n📊 Analysis Results"
190
+ puts "=" * 30
191
+ puts "Overall Score: #{data['summary']['optimization_score']}/100"
192
+ puts "Issues Found: #{data['summary']['issues_found']}"
193
+ puts "Queries Analyzed: #{data['summary']['total_queries']}"
194
+
195
+ if data['n_plus_one']['detected']
196
+ puts "\n🔍 N+1 Query Issues:"
197
+ data['n_plus_one']['patterns'].each do |pattern|
198
+ puts " ⚠️ #{pattern['table']}.#{pattern['column']}"
199
+ puts " 💡 #{pattern['suggestion']}"
200
+ end
201
+ end
202
+
203
+ unless data['slow_queries'].empty?
204
+ puts "\n🐌 Slow Queries:"
205
+ data['slow_queries'].each do |query|
206
+ puts " ⚠️ #{query['duration_ms']}ms: #{query['sql'][0..80]}..."
207
+ query['suggestions'].each do |suggestion|
208
+ puts " 💡 #{suggestion}"
209
+ end
210
+ end
211
+ end
212
+
213
+ unless data['missing_indexes'].empty?
214
+ puts "\n📊 Missing Indexes:"
215
+ data['missing_indexes'].each do |index|
216
+ puts " 💡 #{index['sql']}"
217
+ end
218
+ end
219
+
220
+ if data['summary']['optimization_score'] >= 80
221
+ puts "\n✅ Great job! Your queries are well optimized."
222
+ elsif data['summary']['optimization_score'] >= 60
223
+ puts "\n⚠️ Some optimization opportunities found."
224
+ else
225
+ puts "\n🚨 Significant performance issues detected."
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QueryOptimizerClient
4
+ VERSION = "0.2.0"
5
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "query_optimizer_client/version"
4
+ require_relative "query_optimizer_client/client"
5
+ require_relative "query_optimizer_client/configuration"
6
+ require_relative "query_optimizer_client/middleware"
7
+ require_relative "query_optimizer_client/railtie" if defined?(Rails)
8
+
9
+ module QueryOptimizerClient
10
+ class Error < StandardError; end
11
+ class APIError < Error; end
12
+ class AuthenticationError < Error; end
13
+ class RateLimitError < Error; end
14
+ class ValidationError < Error; end
15
+
16
+ class << self
17
+ attr_accessor :configuration
18
+ end
19
+
20
+ def self.configuration
21
+ @configuration ||= Configuration.new
22
+ end
23
+
24
+ def self.configure
25
+ yield(configuration)
26
+ end
27
+
28
+ def self.client
29
+ @client ||= Client.new(configuration)
30
+ end
31
+
32
+ def self.analyze_queries(queries)
33
+ client.analyze_queries(queries)
34
+ end
35
+
36
+ def self.analyze_for_ci(queries, threshold: 80)
37
+ client.analyze_for_ci(queries, threshold: threshold)
38
+ end
39
+
40
+ def self.enabled?
41
+ configuration.enabled?
42
+ end
43
+
44
+ def self.reset!
45
+ @configuration = nil
46
+ @client = nil
47
+ end
48
+ end
data/lib/tasks/.keep ADDED
File without changes
data/public/robots.txt ADDED
@@ -0,0 +1 @@
1
+ # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/query_optimizer_client/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "QueryWise"
7
+ spec.version = QueryOptimizerClient::VERSION
8
+ spec.authors = ["Blair Lane"]
9
+ spec.email = ["blairjacklane@gmail.com"]
10
+
11
+ spec.summary = "QueryWise - Rails Database Query Optimizer"
12
+ spec.description = "QueryWise is a lightweight, developer-friendly tool that helps Ruby on Rails teams detect, analyze, and fix inefficient database queries. Automatically detect N+1 queries, slow queries, and missing indexes without needing heavy, expensive Application Performance Monitoring (APM) software."
13
+ spec.homepage = "https://github.com/BlairLane22/QueryWise"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.0.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/BlairLane22/QueryWise"
20
+ spec.metadata["changelog_uri"] = "https://github.com/BlairLane22/QueryWise/blob/main/CHANGELOG.md"
21
+ spec.metadata["documentation_uri"] = "https://github.com/BlairLane22/QueryWise/blob/main/README.md"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ Dir.glob("**/*", File::FNM_DOTMATCH).reject do |f|
26
+ File.directory?(f) ||
27
+ f.start_with?('.git/', 'tmp/', 'log/', 'spec/', 'test/', 'features/') ||
28
+ f.match?(/\A\./) ||
29
+ f.end_with?('.gem') ||
30
+ f == 'Gemfile' ||
31
+ f == 'Gemfile.lock' ||
32
+ f == 'Rakefile' ||
33
+ f.include?('docker') ||
34
+ f.include?('scripts/') ||
35
+ f.include?('docs/') ||
36
+ f.start_with?('bin/') ||
37
+ File.expand_path(f) == __FILE__
38
+ end
39
+ end
40
+
41
+ spec.bindir = "exe"
42
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
43
+ spec.require_paths = ["lib"]
44
+
45
+ # Runtime dependencies
46
+ spec.add_dependency "httparty", "~> 0.21"
47
+ spec.add_dependency "activesupport", "~> 7.0"
48
+
49
+ # Development dependencies
50
+ spec.add_development_dependency "rake", "~> 13.0"
51
+ spec.add_development_dependency "rspec", "~> 3.0"
52
+ spec.add_development_dependency "rubocop", "~> 1.21"
53
+ spec.add_development_dependency "webmock", "~> 3.18"
54
+ spec.add_development_dependency "vcr", "~> 6.1"
55
+ spec.add_development_dependency "rails", "~> 7.0"
56
+ spec.add_development_dependency "sqlite3", "~> 1.4"
57
+
58
+ # For more information and examples about making a new gem, check out our
59
+ # guide at: https://bundler.io/guides/creating_gem.html
60
+ end
data/script/.keep ADDED
File without changes
data/storage/.keep ADDED
File without changes
Binary file
Binary file
data/vendor/.keep ADDED
File without changes
metadata ADDED
@@ -0,0 +1,265 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: QueryWise
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Blair Lane
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-08-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.21'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.21'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '7.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '7.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.21'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.21'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.18'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.18'
97
+ - !ruby/object:Gem::Dependency
98
+ name: vcr
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '6.1'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '6.1'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '7.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '7.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sqlite3
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.4'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.4'
139
+ description: QueryWise is a lightweight, developer-friendly tool that helps Ruby on
140
+ Rails teams detect, analyze, and fix inefficient database queries. Automatically
141
+ detect N+1 queries, slow queries, and missing indexes without needing heavy, expensive
142
+ Application Performance Monitoring (APM) software.
143
+ email:
144
+ - blairjacklane@gmail.com
145
+ executables: []
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - CHANGELOG.md
150
+ - CLOUD_RUN_README.md
151
+ - DOCKER_README.md
152
+ - Dockerfile
153
+ - Dockerfile.cloudrun
154
+ - Dockerfile.dev
155
+ - GEM_Gemfile
156
+ - GEM_README.md
157
+ - GEM_Rakefile
158
+ - GEM_gitignore
159
+ - LICENSE.txt
160
+ - PUBLISHING_GUIDE.md
161
+ - README.md
162
+ - app.yaml
163
+ - app/controllers/api/v1/analysis_controller.rb
164
+ - app/controllers/api/v1/api_keys_controller.rb
165
+ - app/controllers/api/v1/base_controller.rb
166
+ - app/controllers/api/v1/health_controller.rb
167
+ - app/controllers/application_controller.rb
168
+ - app/controllers/concerns/.keep
169
+ - app/jobs/application_job.rb
170
+ - app/mailers/application_mailer.rb
171
+ - app/models/app_profile.rb
172
+ - app/models/application_record.rb
173
+ - app/models/concerns/.keep
174
+ - app/models/optimization_suggestion.rb
175
+ - app/models/query_analysis.rb
176
+ - app/models/query_pattern.rb
177
+ - app/services/missing_index_detector_service.rb
178
+ - app/services/n_plus_one_detector_service.rb
179
+ - app/services/slow_query_analyzer_service.rb
180
+ - app/services/sql_parser_service.rb
181
+ - app/validators/query_data_validator.rb
182
+ - app/views/layouts/mailer.html.erb
183
+ - app/views/layouts/mailer.text.erb
184
+ - cloudbuild.yaml
185
+ - config.ru
186
+ - config/application.rb
187
+ - config/boot.rb
188
+ - config/cable.yml
189
+ - config/cache.yml
190
+ - config/credentials.yml.enc
191
+ - config/database.yml
192
+ - config/deploy.yml
193
+ - config/environment.rb
194
+ - config/environments/development.rb
195
+ - config/environments/production.rb
196
+ - config/environments/test.rb
197
+ - config/initializers/cors.rb
198
+ - config/initializers/filter_parameter_logging.rb
199
+ - config/initializers/inflections.rb
200
+ - config/locales/en.yml
201
+ - config/master.key
202
+ - config/puma.rb
203
+ - config/puma_cloudrun.rb
204
+ - config/queue.yml
205
+ - config/recurring.yml
206
+ - config/routes.rb
207
+ - config/storage.yml
208
+ - db/cable_schema.rb
209
+ - db/cache_schema.rb
210
+ - db/migrate/20250818214709_create_app_profiles.rb
211
+ - db/migrate/20250818214731_create_query_analyses.rb
212
+ - db/migrate/20250818214740_create_query_patterns.rb
213
+ - db/migrate/20250818214805_create_optimization_suggestions.rb
214
+ - db/queue_schema.rb
215
+ - db/schema.rb
216
+ - db/seeds.rb
217
+ - init.sql
218
+ - lib/query_optimizer_client.rb
219
+ - lib/query_optimizer_client/client.rb
220
+ - lib/query_optimizer_client/configuration.rb
221
+ - lib/query_optimizer_client/generators/install_generator.rb
222
+ - lib/query_optimizer_client/generators/templates/README
223
+ - lib/query_optimizer_client/generators/templates/analysis_job.rb
224
+ - lib/query_optimizer_client/generators/templates/initializer.rb
225
+ - lib/query_optimizer_client/middleware.rb
226
+ - lib/query_optimizer_client/railtie.rb
227
+ - lib/query_optimizer_client/tasks.rake
228
+ - lib/query_optimizer_client/version.rb
229
+ - lib/tasks/.keep
230
+ - public/robots.txt
231
+ - query_optimizer_client.gemspec
232
+ - script/.keep
233
+ - storage/.keep
234
+ - storage/development.sqlite3
235
+ - storage/test.sqlite3
236
+ - vendor/.keep
237
+ homepage: https://github.com/BlairLane22/QueryWise
238
+ licenses:
239
+ - MIT
240
+ metadata:
241
+ allowed_push_host: https://rubygems.org
242
+ homepage_uri: https://github.com/BlairLane22/QueryWise
243
+ source_code_uri: https://github.com/BlairLane22/QueryWise
244
+ changelog_uri: https://github.com/BlairLane22/QueryWise/blob/main/CHANGELOG.md
245
+ documentation_uri: https://github.com/BlairLane22/QueryWise/blob/main/README.md
246
+ post_install_message:
247
+ rdoc_options: []
248
+ require_paths:
249
+ - lib
250
+ required_ruby_version: !ruby/object:Gem::Requirement
251
+ requirements:
252
+ - - ">="
253
+ - !ruby/object:Gem::Version
254
+ version: 3.0.0
255
+ required_rubygems_version: !ruby/object:Gem::Requirement
256
+ requirements:
257
+ - - ">="
258
+ - !ruby/object:Gem::Version
259
+ version: '0'
260
+ requirements: []
261
+ rubygems_version: 3.0.3.1
262
+ signing_key:
263
+ specification_version: 4
264
+ summary: QueryWise - Rails Database Query Optimizer
265
+ test_files: []