detector 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 78362ec4d3b8705fcb95855b07747ac13ad8dc03942743ace4ab9699659c7b6e
4
+ data.tar.gz: 5efa218d244399e55a5d31aa8480c96b1434509fe0fe2b8bf690812b5279c7aa
5
+ SHA512:
6
+ metadata.gz: 2b3b958c50d44739c8f7825ec4d0b1db7383e866cd62f05ffc2957b5afa8886ca5bc50e329949c097155429c37175e66fb1b7a182ef6606a5925fdd30bc15517
7
+ data.tar.gz: 2f26cec088bc720b9b6644a0bd6bb01f0ed07676887170481ec6a8d7c94d7096a35f0c136a6e4677fb103456aa4938265e51ae41107ed8e74cd0e5efbdc5b73c
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jonathan Siegel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # Detector
2
+
3
+ A Ruby gem for detecting and analyzing various database systems. Detector is a system manager's toolkit that helps you quickly check database stats and structure.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'detector'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```
22
+ $ gem install detector
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### CLI
28
+
29
+ ```
30
+ $ detector "postgres://user:pass@host:port/dbname"
31
+ ```
32
+
33
+ This will display:
34
+ - Database system type
35
+ - Version
36
+ - Database count
37
+ - For the 3 largest databases:
38
+ - Table count
39
+ - The 3 largest tables with row counts
40
+
41
+ ### Ruby API
42
+
43
+ ```ruby
44
+ require 'detector'
45
+
46
+ # Create a detector for a database
47
+ db = Detector.detect("postgres://user:pass@host:port/dbname")
48
+
49
+ # Get basic info
50
+ db.kind # => :postgres
51
+ db.host # => "host"
52
+ db.port # => 5432
53
+ db.version # => "PostgreSQL 12.1 on x86_64-pc-linux-gnu, ..."
54
+
55
+ # Get database stats
56
+ db.database_count # => 5
57
+ db.databases # => [{ name: "db1", size: "1.2 GB", ... }, ...]
58
+
59
+ # Get table stats (requires database name)
60
+ db_name = db.databases.first[:name] # Or any database you want to inspect
61
+ db.table_count(db_name) # => 42
62
+ db.tables(db_name) # => [{ name: "users", row_count: 10000, size: "500 MB", ... }, ...]
63
+ ```
64
+
65
+ ## Supported Systems
66
+
67
+ - PostgreSQL
68
+ - MySQL
69
+ - MariaDB
70
+ - Redis
71
+ - SMTP
72
+
73
+ ## License
74
+
75
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/bin/detector ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "detector"
5
+
6
+ if ARGV.empty?
7
+ puts "Usage: detector <URI>"
8
+ puts "Example: detector \"postgres://user:pass@host:port/dbname\""
9
+ exit 1
10
+ end
11
+
12
+ uri = ARGV[0]
13
+ detector = Detector.detect(uri)
14
+
15
+ if detector.nil?
16
+ puts "Invalid or unsupported URI: #{uri}"
17
+ exit 1
18
+ end
19
+
20
+ puts "Detected: #{detector.kind}"
21
+ puts "Version: #{detector.version}"
22
+ puts "Host: #{detector.host}:#{detector.port}"
23
+
24
+ if detector.databases?
25
+ db_count = detector.database_count
26
+ puts "\nDatabases: #{db_count}"
27
+
28
+ if db_count && db_count > 0
29
+ dbs = detector.databases.first(3)
30
+ dbs.each do |db|
31
+ db_name = db[:name]
32
+ puts "\nDatabase: #{db_name} (#{db[:size]})"
33
+
34
+ if detector.tables?
35
+ tables = detector.tables(db_name).first(3)
36
+ puts " Tables: #{detector.table_count(db_name)}"
37
+
38
+ tables.each do |table|
39
+ puts " - #{table[:name]}: #{table[:row_count]} rows (#{table[:size]})"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,88 @@
1
+ require_relative 'mysql'
2
+
3
+ module Detector
4
+ module Addons
5
+ class MariaDB < MySQL
6
+ def self.handles_uri?(uri)
7
+ uri.scheme.downcase == 'mariadb'
8
+ end
9
+
10
+ def self.capabilities_for(url)
11
+ { sql: true, kv: true, url: url, kind: :mariadb, databases: true, tables: true }
12
+ end
13
+
14
+ def version
15
+ return nil unless info
16
+ "MariaDB #{info['version']} on #{info['database']} (#{info['user']})"
17
+ end
18
+
19
+ def databases
20
+ return [] unless connection
21
+ begin
22
+ # First get all databases
23
+ db_list = connection.query("SELECT schema_name AS name
24
+ FROM information_schema.SCHEMATA
25
+ WHERE schema_name NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')").map do |row|
26
+ row['name']
27
+ end
28
+
29
+ # For each database, get its size
30
+ result = []
31
+ db_list.each do |db_name|
32
+ size_query = "SELECT
33
+ IFNULL(FORMAT(SUM(data_length + index_length) / 1024 / 1024, 2), '0.00') AS size_mb,
34
+ IFNULL(SUM(data_length + index_length), 0) AS raw_size
35
+ FROM information_schema.TABLES
36
+ WHERE table_schema = '#{db_name}'"
37
+
38
+ size_data = connection.query(size_query).first
39
+ result << {
40
+ name: db_name,
41
+ size: "#{size_data['size_mb']} MB",
42
+ raw_size: size_data['raw_size'].to_i
43
+ }
44
+ end
45
+
46
+ # Sort by size
47
+ @databases = result.sort_by { |db| -db[:raw_size] }
48
+ rescue => e
49
+ puts "Error getting databases: #{e.message}"
50
+ []
51
+ end
52
+ end
53
+
54
+ def tables(database_name)
55
+ return [] unless connection
56
+
57
+ begin
58
+ @tables ||= {}
59
+ @tables[database_name] ||= connection.query("SELECT
60
+ table_name AS name,
61
+ IFNULL(FORMAT((data_length + index_length) / 1024 / 1024, 2), '0.00') AS size_mb,
62
+ IFNULL((data_length + index_length), 0) AS raw_size,
63
+ IFNULL(table_rows, 0) AS row_count
64
+ FROM information_schema.TABLES
65
+ WHERE table_schema = '#{database_name}'
66
+ ORDER BY raw_size DESC").map do |row|
67
+ {
68
+ name: row['name'],
69
+ size: "#{row['size_mb']} MB",
70
+ raw_size: row['raw_size'].to_i,
71
+ row_count: row['row_count'].to_i
72
+ }
73
+ end
74
+ rescue => e
75
+ puts "Error getting tables for #{database_name}: #{e.message}"
76
+ []
77
+ end
78
+ end
79
+
80
+ def cli_name
81
+ "mariadb"
82
+ end
83
+ end
84
+ end
85
+
86
+ # Register the addon
87
+ Base.register_addon(:mariadb, Addons::MariaDB)
88
+ end
@@ -0,0 +1,97 @@
1
+ require 'mysql2'
2
+
3
+ module Detector
4
+ module Addons
5
+ class MySQL < Base
6
+ def self.handles_uri?(uri)
7
+ uri.scheme.downcase == 'mysql'
8
+ end
9
+
10
+ def self.capabilities_for(url)
11
+ { sql: true, kv: true, url: url, kind: :mysql, databases: true, tables: true }
12
+ end
13
+
14
+ def connection
15
+ @conn ||= Mysql2::Client.new(
16
+ host: host,
17
+ username: uri.user,
18
+ password: uri.password,
19
+ database: uri.path[1..-1],
20
+ port: port
21
+ ) rescue nil
22
+ end
23
+
24
+ def info
25
+ return nil unless connection
26
+ @info ||= connection.query("SELECT VERSION() AS version, DATABASE() AS `database`, USER() AS user").first
27
+ end
28
+
29
+ def version
30
+ return nil unless info
31
+ "MySQL #{info['version']} on #{info['database']} (#{info['user']})"
32
+ end
33
+
34
+ def usage
35
+ return nil unless connection && info
36
+ result = connection.query("SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size FROM information_schema.TABLES WHERE table_schema = '#{info['database']}'").first
37
+ "#{result['size']} MB"
38
+ end
39
+
40
+ def database_count
41
+ return nil unless connection
42
+ @database_count ||= connection.query("SELECT COUNT(*) AS count FROM information_schema.SCHEMATA WHERE schema_name NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')").first['count']
43
+ end
44
+
45
+ def databases
46
+ return [] unless connection
47
+ @databases ||= connection.query("SELECT schema_name AS name,
48
+ FORMAT(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb,
49
+ SUM(data_length + index_length) AS raw_size
50
+ FROM information_schema.SCHEMATA
51
+ JOIN information_schema.TABLES ON table_schema = schema_name
52
+ WHERE schema_name NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
53
+ GROUP BY schema_name
54
+ ORDER BY raw_size DESC").map do |row|
55
+ { name: row['name'], size: "#{row['size_mb']} MB", raw_size: row['raw_size'].to_i }
56
+ end
57
+ end
58
+
59
+ def table_count(database_name)
60
+ return nil unless connection
61
+ connection.query("SELECT COUNT(*) AS count FROM information_schema.TABLES WHERE table_schema = '#{database_name}'").first['count']
62
+ end
63
+
64
+ def tables(database_name)
65
+ return [] unless connection
66
+
67
+ @tables ||= {}
68
+ @tables[database_name] ||= connection.query("SELECT table_name AS name,
69
+ FORMAT((data_length + index_length) / 1024 / 1024, 2) AS size_mb,
70
+ (data_length + index_length) AS raw_size,
71
+ table_rows AS row_count
72
+ FROM information_schema.TABLES
73
+ WHERE table_schema = '#{database_name}'
74
+ ORDER BY raw_size DESC").map do |row|
75
+ { name: row['name'], size: "#{row['size_mb']} MB", raw_size: row['raw_size'].to_i, row_count: row['row_count'].to_i }
76
+ end
77
+ end
78
+
79
+ def connection_count
80
+ return nil unless connection
81
+ connection.query("SELECT COUNT(*) AS count FROM information_schema.PROCESSLIST").first['count']
82
+ end
83
+
84
+ def connection_limit
85
+ return nil unless connection
86
+ connection.query("SHOW VARIABLES LIKE 'max_connections'").first['Value'].to_i
87
+ end
88
+
89
+ def cli_name
90
+ "mysql"
91
+ end
92
+ end
93
+ end
94
+
95
+ # Register the addon
96
+ Base.register_addon(:mysql, Addons::MySQL)
97
+ end
@@ -0,0 +1,122 @@
1
+ require 'pg'
2
+
3
+ module Detector
4
+ module Addons
5
+ class Postgres < Base
6
+ def self.handles_uri?(uri)
7
+ uri.scheme.downcase =~ /postgres/
8
+ end
9
+
10
+ def self.capabilities_for(url)
11
+ { sql: true, kv: true, url: url, kind: :postgres, databases: true, tables: true }
12
+ end
13
+
14
+ def connection
15
+ @conn ||= PG::Connection.new(uri) rescue nil
16
+ end
17
+
18
+ def version
19
+ return nil unless connection
20
+ @version ||= connection.exec("SELECT version()").first['version']
21
+ end
22
+
23
+ def usage
24
+ return nil unless connection
25
+ connection.exec("SELECT pg_size_pretty(pg_database_size(current_database())) AS size").first['size']
26
+ end
27
+
28
+ def table_count(database_name)
29
+ return nil unless connection
30
+
31
+ # If we need to query a different database, temporarily connect to it
32
+ if database_name != current_database
33
+ # Create a temporary connection to the specified database
34
+ temp_conn = PG::Connection.new(host: host, port: port, user: uri.user,
35
+ password: uri.password, dbname: database_name) rescue nil
36
+ return nil unless temp_conn
37
+
38
+ count = temp_conn.exec("SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public'").first['count'].to_i
39
+ temp_conn.close
40
+ return count
41
+ end
42
+
43
+ # Query the current database
44
+ @table_count ||= connection.exec("SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public'").first['count'].to_i
45
+ end
46
+
47
+ def current_database
48
+ @current_db ||= connection.exec("SELECT current_database()").first['current_database']
49
+ end
50
+
51
+ def tables(database_name)
52
+ return [] unless connection
53
+
54
+ # If we need to query a different database, temporarily connect to it
55
+ if database_name != current_database
56
+ # Create a temporary connection to the specified database
57
+ temp_conn = PG::Connection.new(host: host, port: port, user: uri.user,
58
+ password: uri.password, dbname: database_name) rescue nil
59
+ return [] unless temp_conn
60
+
61
+ result = temp_conn.exec("SELECT table_name,
62
+ pg_size_pretty(pg_total_relation_size(quote_ident(table_name))) as size,
63
+ pg_total_relation_size(quote_ident(table_name)) as raw_size,
64
+ (SELECT reltuples::bigint FROM pg_class WHERE relname = table_name) as row_count
65
+ FROM information_schema.tables
66
+ WHERE table_schema = 'public'
67
+ ORDER BY raw_size DESC").map do |row|
68
+ { name: row['table_name'], size: row['size'], raw_size: row['raw_size'].to_i, row_count: row['row_count'].to_i }
69
+ end
70
+
71
+ temp_conn.close
72
+ return result
73
+ end
74
+
75
+ # Query the current database
76
+ @tables ||= {}
77
+ @tables[database_name] ||= connection.exec("SELECT table_name,
78
+ pg_size_pretty(pg_total_relation_size(quote_ident(table_name))) as size,
79
+ pg_total_relation_size(quote_ident(table_name)) as raw_size,
80
+ (SELECT reltuples::bigint FROM pg_class WHERE relname = table_name) as row_count
81
+ FROM information_schema.tables
82
+ WHERE table_schema = 'public'
83
+ ORDER BY raw_size DESC").map do |row|
84
+ { name: row['table_name'], size: row['size'], raw_size: row['raw_size'].to_i, row_count: row['row_count'].to_i }
85
+ end
86
+ end
87
+
88
+ def database_count
89
+ return nil unless connection
90
+ @database_count ||= connection.exec("SELECT count(*) FROM pg_database WHERE datistemplate = false").first['count'].to_i
91
+ end
92
+
93
+ def databases
94
+ return [] unless connection
95
+ @databases ||= connection.exec("SELECT datname, pg_size_pretty(pg_database_size(datname)) as size,
96
+ pg_database_size(datname) as raw_size
97
+ FROM pg_database
98
+ WHERE datistemplate = false
99
+ ORDER BY raw_size DESC").map do |row|
100
+ { name: row['datname'], size: row['size'], raw_size: row['raw_size'].to_i }
101
+ end
102
+ end
103
+
104
+ def connection_count
105
+ return nil unless connection
106
+ connection.exec("SELECT count(*) FROM pg_stat_activity").first['count'].to_i
107
+ end
108
+
109
+ def connection_limit
110
+ return nil unless connection
111
+ connection.exec("SELECT current_setting('max_connections')").first['current_setting'].to_i
112
+ end
113
+
114
+ def cli_name
115
+ "psql"
116
+ end
117
+ end
118
+ end
119
+
120
+ # Register the addon
121
+ Base.register_addon(:postgres, Addons::Postgres)
122
+ end
@@ -0,0 +1,79 @@
1
+ require 'redis'
2
+
3
+ module Detector
4
+ module Addons
5
+ class Redis < Base
6
+ def self.handles_uri?(uri)
7
+ uri.scheme.downcase == 'redis' || uri.scheme.downcase == 'rediss'
8
+ end
9
+
10
+ def self.capabilities_for(url)
11
+ { kv: true, sql: false, url: url, kind: :redis, databases: true, tables: false }
12
+ end
13
+
14
+ def connection
15
+ return @conn if @conn
16
+
17
+ if uri.scheme == 'rediss'
18
+ @conn = ::Redis.new(url: @url, port: uri.port, timeout: 5.0, ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }) rescue nil
19
+ else
20
+ @conn = ::Redis.new(url: @url, timeout: 5.0) rescue nil
21
+ end
22
+ end
23
+
24
+ def info
25
+ return nil unless connection
26
+ @info ||= connection.info
27
+ end
28
+
29
+ def version
30
+ return nil unless info
31
+ "Redis #{info['redis_version']} on #{info['os']} #{info['arch']}, compiled by #{info['gcc_version']}, #{info['arch_bits']}-bit"
32
+ end
33
+
34
+ def usage
35
+ return nil unless info
36
+
37
+ per = (info['used_memory'].to_f / info['maxmemory'].to_f) * 100
38
+ percent = sprintf("%.2f%%", per)
39
+ "#{info['used_memory_human']} of #{info['maxmemory_human']} used (#{percent})" rescue -1
40
+ end
41
+
42
+ def database_count
43
+ return nil unless connection
44
+ connection.info['keyspace'].keys.size
45
+ end
46
+
47
+ def databases
48
+ return [] unless info && info['keyspace']
49
+
50
+ info['keyspace'].map do |db_name, stats|
51
+ keys, expires = stats.split(',').map { |s| s.split('=').last.to_i }
52
+ { name: db_name, keys: keys, expires: expires, stats: stats }
53
+ end.sort_by { |db| -db[:keys] }
54
+ end
55
+
56
+ def table_count
57
+ return nil unless connection
58
+ connection.dbsize rescue 0
59
+ end
60
+
61
+ def connection_count
62
+ return nil unless info
63
+ info['connected_clients'].to_i rescue 0
64
+ end
65
+
66
+ def connection_limit
67
+ return nil unless info
68
+ info['maxclients'].to_i rescue 0
69
+ end
70
+
71
+ def cli_name
72
+ "redis-cli"
73
+ end
74
+ end
75
+ end
76
+
77
+ # Register the addon
78
+ Base.register_addon(:redis, Addons::Redis)
79
+ end
@@ -0,0 +1,40 @@
1
+ require 'net/smtp'
2
+
3
+ module Detector
4
+ module Addons
5
+ class SMTP < Base
6
+ def self.handles_uri?(uri)
7
+ uri.scheme.downcase == 'smtp' || uri.scheme.downcase == 'smtps'
8
+ end
9
+
10
+ def self.capabilities_for(url)
11
+ { kv: true, sql: false, url: url, kind: :smtp, databases: false, tables: false }
12
+ end
13
+
14
+ def connection
15
+ return @conn if @conn
16
+
17
+ begin
18
+ @conn = Net::SMTP.new(host, port)
19
+ @conn.open_timeout = 5
20
+ @conn.start('detector.local', uri.user, uri.password, :login)
21
+ @conn
22
+ rescue => e
23
+ nil
24
+ end
25
+ end
26
+
27
+ def version
28
+ return nil unless connection
29
+ "SMTP server at #{host}:#{port}"
30
+ end
31
+
32
+ def cli_name
33
+ "telnet"
34
+ end
35
+ end
36
+ end
37
+
38
+ # Register the addon
39
+ Base.register_addon(:smtp, Addons::SMTP)
40
+ end
@@ -0,0 +1,142 @@
1
+ require 'socket'
2
+
3
+ module Detector
4
+ class Base
5
+ @@addons = {}
6
+
7
+ def self.register_addon(kind, klass)
8
+ @@addons[kind] = klass
9
+ end
10
+
11
+ def self.detect(val)
12
+ return nil unless val =~ /\A#{URI::regexp}\z/
13
+
14
+ begin
15
+ uri = URI.parse(val)
16
+ rescue => e
17
+ puts "Error parsing URI: #{e.class} #{e.message}"
18
+ return nil
19
+ end
20
+
21
+ # Try each registered addon to see if it can handle this URI
22
+ @@addons.each do |kind, klass|
23
+ if klass.handles_uri?(uri)
24
+ detector = klass.new(val)
25
+ return detector if detector.valid?
26
+ end
27
+ end
28
+
29
+ # Fallback to generic handling if no addon matched
30
+ nil
31
+ end
32
+
33
+ # Default implementation to be overridden by subclasses
34
+ def self.handles_uri?(uri)
35
+ false
36
+ end
37
+
38
+ # Default implementation to get capabilities, should be overridden by subclasses
39
+ def self.capabilities_for(url)
40
+ nil
41
+ end
42
+
43
+ def initialize(url)
44
+ @url = url
45
+ @capabilities = self.class.capabilities_for(url)
46
+ @keys = []
47
+ end
48
+
49
+ attr_accessor :uri, :keys
50
+
51
+ def sql?
52
+ valid? && @capabilities[:sql]
53
+ end
54
+
55
+ def valid?
56
+ @capabilities && @capabilities[:kind]
57
+ end
58
+
59
+ def kind
60
+ @capabilities && @capabilities[:kind]
61
+ end
62
+
63
+ def databases?
64
+ @capabilities && @capabilities[:databases]
65
+ end
66
+
67
+ def tables?
68
+ @capabilities && @capabilities[:tables]
69
+ end
70
+
71
+ def summary
72
+ return "Invalid URI" unless valid?
73
+ "#{kind} in #{host}"
74
+ end
75
+
76
+ def uri
77
+ @uri ||= URI.parse(@url)
78
+ end
79
+
80
+ def host
81
+ return nil unless valid?
82
+ uri.host
83
+ end
84
+
85
+ def port
86
+ return nil unless valid?
87
+ uri.port
88
+ end
89
+
90
+ def ip
91
+ return nil unless valid?
92
+ Resolv.getaddress(host)
93
+ rescue
94
+ nil
95
+ end
96
+
97
+ def connection?
98
+ connection.present?
99
+ end
100
+
101
+ def connection
102
+ nil
103
+ end
104
+
105
+ def ping
106
+ return nil unless valid?
107
+ tcp_test
108
+ end
109
+
110
+ def tcp_test
111
+ TCPSocket.new(ip, port).present? rescue nil
112
+ end
113
+
114
+ def table_count
115
+ nil
116
+ end
117
+
118
+ def database_count
119
+ nil
120
+ end
121
+
122
+ def databases
123
+ []
124
+ end
125
+
126
+ def tables(database_name)
127
+ []
128
+ end
129
+
130
+ def cli_name
131
+ nil
132
+ end
133
+
134
+ def version
135
+ nil
136
+ end
137
+
138
+ def usage
139
+ nil
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,3 @@
1
+ module Detector
2
+ VERSION = "0.1.0"
3
+ end
data/lib/detector.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'uri'
2
+ require 'resolv'
3
+ require 'detector/version'
4
+ require 'detector/base'
5
+ require 'detector/addons/postgres'
6
+ require 'detector/addons/redis'
7
+ require 'detector/addons/mysql'
8
+ require 'detector/addons/mariadb'
9
+ require 'detector/addons/smtp'
10
+
11
+ module Detector
12
+ class Error < StandardError; end
13
+
14
+ def self.detect(uri_string)
15
+ Base.detect(uri_string)
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: detector
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Siegel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-04-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: uri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.11.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.11.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mysql2
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.5'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: resolv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: bigdecimal
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: net-smtp
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.3.3
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.3.3
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.10'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.10'
125
+ description: A system manager's toolkit to detect and analyze various database systems
126
+ like Postgres, MySQL, Redis, etc.
127
+ email:
128
+ - "<248302+usiegj00@users.noreply.github.com>"
129
+ executables:
130
+ - detector
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - LICENSE
135
+ - README.md
136
+ - bin/detector
137
+ - lib/detector.rb
138
+ - lib/detector/addons/mariadb.rb
139
+ - lib/detector/addons/mysql.rb
140
+ - lib/detector/addons/postgres.rb
141
+ - lib/detector/addons/redis.rb
142
+ - lib/detector/addons/smtp.rb
143
+ - lib/detector/base.rb
144
+ - lib/detector/version.rb
145
+ homepage: https://github.com/usiegj00/detector
146
+ licenses:
147
+ - MIT
148
+ metadata:
149
+ homepage_uri: https://github.com/usiegj00/detector
150
+ source_code_uri: https://github.com/usiegj00/detector
151
+ changelog_uri: https://github.com/usiegj00/detector/blob/main/CHANGELOG.md
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: 2.6.0
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubygems_version: 3.5.16
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: Detect and analyze various database systems
171
+ test_files: []