Class: Detector::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/detector/base.rb

Constant Summary collapse

@@addons =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url) ⇒ Base

Returns a new instance of Base.



46
47
48
49
50
# File 'lib/detector/base.rb', line 46

def initialize(url)
  @url = url
  @capabilities = self.class.capabilities_for(url)
  @keys = []
end

Instance Attribute Details

#keysObject

Returns the value of attribute keys.



52
53
54
# File 'lib/detector/base.rb', line 52

def keys
  @keys
end

#uriObject

Returns the value of attribute uri.



52
53
54
# File 'lib/detector/base.rb', line 52

def uri
  @uri
end

Class Method Details

.capabilities_for(url) ⇒ Object

Default implementation to get capabilities, should be overridden by subclasses



42
43
44
# File 'lib/detector/base.rb', line 42

def self.capabilities_for(url)
  nil
end

.detect(val) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/detector/base.rb', line 14

def self.detect(val)
  return nil unless val =~ /\A#{URI::regexp}\z/
  
  begin
    uri = URI.parse(val)
  rescue => e
    puts "Error parsing URI: #{e.class} #{e.message}"
    return nil
  end
  
  # Try each registered addon to see if it can handle this URI
  @@addons.each do |kind, klass|
    if klass.handles_uri?(uri)
      detector = klass.new(val)
      return detector if detector.valid?
    end
  end
  
  # Fallback to generic handling if no addon matched
  nil
end

.handles_uri?(uri) ⇒ Boolean

Default implementation to be overridden by subclasses

Returns:

  • (Boolean)


37
38
39
# File 'lib/detector/base.rb', line 37

def self.handles_uri?(uri)
  false
end

.register_addon(kind, klass) ⇒ Object



10
11
12
# File 'lib/detector/base.rb', line 10

def self.register_addon(kind, klass)
  @@addons[kind] = klass
end

Instance Method Details

#asnObject



116
117
118
119
# File 'lib/detector/base.rb', line 116

def asn
  return nil unless valid?
  geo&.data&.dig('org')&.split(" ")&.first
end

#cli_nameObject



207
208
209
# File 'lib/detector/base.rb', line 207

def cli_name
  nil
end

#closeObject



264
265
266
267
# File 'lib/detector/base.rb', line 264

def close
  # Default implementation does nothing
  # Subclasses should override to close any open connections
end

#connectionObject



125
126
127
128
129
130
# File 'lib/detector/base.rb', line 125

def connection
  # Default implementation returns nil
  # Each addon should implement its own connection method
  # that creates a new connection for each request
  nil
end

#connection?Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/detector/base.rb', line 121

def connection?
  connection.present?
end

#connection_countObject



238
239
240
# File 'lib/detector/base.rb', line 238

def connection_count
  nil
end

#connection_infoObject



251
252
253
254
255
256
257
258
# File 'lib/detector/base.rb', line 251

def connection_info
  # Default implementation for databases without user-specific limits
  return nil unless connection_count && connection_limit
  {
    connection_count: { user: connection_count, global: connection_count },
    connection_limits: { user: connection_limit, global: connection_limit }
  }
end

#connection_limitObject



242
243
244
# File 'lib/detector/base.rb', line 242

def connection_limit
  nil
end

#connection_usage_percentageObject



246
247
248
249
# File 'lib/detector/base.rb', line 246

def connection_usage_percentage
  return nil unless connection_count && connection_limit && connection_limit > 0
  (connection_count.to_f / connection_limit.to_f * 100).round(1)
end

#database_countObject



195
196
197
# File 'lib/detector/base.rb', line 195

def database_count
  nil
end

#databasesObject



199
200
201
# File 'lib/detector/base.rb', line 199

def databases
  []
end

#databases?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/detector/base.rb', line 66

def databases?
  @capabilities && @capabilities[:databases]
end

#estimated_row_count(table:, database: nil) ⇒ Object



260
261
262
# File 'lib/detector/base.rb', line 260

def estimated_row_count(table:, database: nil)
  nil
end

#geoObject



100
101
102
103
# File 'lib/detector/base.rb', line 100

def geo
  return nil unless valid?
  @geo ||= Geocoder.search(ip).first
end

#geographyObject

Lookup the location for the IP:



106
107
108
109
# File 'lib/detector/base.rb', line 106

def geography
  return nil unless valid?
  "#{geo.city}, #{geo.region}, #{geo.country}" if geo
end

#hostObject



83
84
85
86
# File 'lib/detector/base.rb', line 83

def host
  return nil unless valid?
  uri.host
end

#infrastructureObject



224
225
226
227
228
229
230
231
232
# File 'lib/detector/base.rb', line 224

def infrastructure
  return nil unless valid?
  
  provider = Vendor.detect_provider(host)
  return provider if provider
  
  # If geo data available, return organization, otherwise nil
  geo&.data&.dig('org')
end

#ipObject



93
94
95
96
97
98
# File 'lib/detector/base.rb', line 93

def ip
  return nil unless valid?
  Resolv.getaddress(host)
rescue
  nil
end

#kindObject



62
63
64
# File 'lib/detector/base.rb', line 62

def kind
  @capabilities && @capabilities[:kind]
end

#pingObject



144
145
146
147
# File 'lib/detector/base.rb', line 144

def ping
  return nil unless valid?
  transport?
end

#portObject



88
89
90
91
# File 'lib/detector/base.rb', line 88

def port
  return nil unless valid?
  uri.port
end

#protocol_typeObject

Should be implemented by subclasses



154
155
156
# File 'lib/detector/base.rb', line 154

def protocol_type
  :tcp # Default to TCP
end

#regionObject



111
112
113
114
# File 'lib/detector/base.rb', line 111

def region
  return nil unless valid?
  Region.detect_region(host, geo)
end

#replication_available?Boolean

Returns:

  • (Boolean)


234
235
236
# File 'lib/detector/base.rb', line 234

def replication_available?
  nil
end

#sql?Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/detector/base.rb', line 54

def sql?
  valid? && @capabilities[:sql]
end

#summaryObject



74
75
76
77
# File 'lib/detector/base.rb', line 74

def summary
  return "Invalid URI" unless valid?
  "#{kind} in #{host}"
end

#table_countObject



182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/detector/base.rb', line 182

def table_count
  return nil unless valid? && tables? && connection?
  
  count = 0
  database_list = databases
  
  database_list.each do |db|
    count += tables(db).size
  end
  
  count.zero? ? nil : count
end

#tables(database_name) ⇒ Object



203
204
205
# File 'lib/detector/base.rb', line 203

def tables(database_name)
  []
end

#tables?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/detector/base.rb', line 70

def tables?
  @capabilities && @capabilities[:tables]
end

#tcp_testObject



158
159
160
161
162
163
164
165
166
167
# File 'lib/detector/base.rb', line 158

def tcp_test
  return nil unless ip && port
  begin
    socket = TCPSocket.new(ip, port)
    socket.close
    true
  rescue => e
    nil
  end
end

#transport?Boolean

Returns:

  • (Boolean)


149
150
151
# File 'lib/detector/base.rb', line 149

def transport?
  protocol_type == :tcp ? tcp_test : udp_test
end

#udp_testObject



169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/detector/base.rb', line 169

def udp_test
  return nil unless ip && port
  begin
    socket = UDPSocket.new
    socket.connect(ip, port)
    socket.send("", 0) # Send empty packet as probe
    socket.close
    true
  rescue => e
    nil
  end
end

#usageObject



215
216
217
# File 'lib/detector/base.rb', line 215

def usage
  nil
end

#user_access_levelObject



219
220
221
222
# File 'lib/detector/base.rb', line 219

def user_access_level
  return nil unless valid? && connection?
  "Unknown"
end

#valid?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/detector/base.rb', line 58

def valid?
  @capabilities && @capabilities[:kind]
end

#versionObject



211
212
213
# File 'lib/detector/base.rb', line 211

def version
  nil
end

#with_connectionObject



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/detector/base.rb', line 132

def with_connection
  conn = connection
  return yield(nil) unless conn
  
  begin
    result = yield(conn)
    return result
  ensure
    close
  end
end