console_kit 1.1.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acd47ead1008fc3bcdc25b0d995b735f69d1e6f5a8d5f8442c67dc5ee86bb9a3
4
- data.tar.gz: 55bd88158a92c6741eab85bda17d4b61828dd101e94cd5ab02097688f8994302
3
+ metadata.gz: 348d99d0b8d74a78d96ffa4e74f7ee2baf25aab659700b5d44929d2e3cd87104
4
+ data.tar.gz: 9c5e7514f498818e2ffe8b0a9376511cbf364e81336a266df7fe02d143a26cf4
5
5
  SHA512:
6
- metadata.gz: 9d1214289e7859342493f2930557554e05ad3ccb3200e041d3ade9b2f95a710b6f111eaea9bfc40f0192e98cabd1010f7be6c71469066d2fd702a74d70a33132
7
- data.tar.gz: 4564b76383af8ab6d60c6ee79644fc78a1be8633520ffe1c74a26a69936ebbacb7db8b20dd84c28c2433f532198c46c1c140ff7d31cd323d613559624fabb713
6
+ metadata.gz: 5e56f04db89485e9b0a3a23b667062a8998287a8ab9ad67dc793f3202730ba6f54dcf02f45ab65ff739e3aef23ec7c65574ba32c4aafe7cebc1a6dcff63c907f
7
+ data.tar.gz: 86d6d4457bb2ad62e8ac2ecdd22c3afc0fa72313f4625cf4b304bbd1f0436a67033e0a2c4190d995895b12aba4aef77c68465ffa1fe1dcf6815d93909f355394
data/.DS_Store ADDED
Binary file
data/.reek.yml.new ADDED
File without changes
@@ -3,20 +3,23 @@
3
3
  module ConsoleKit
4
4
  # Stores ConsoleKit configurations such as tenant map and context behavior
5
5
  class Configuration
6
- attr_accessor :pretty_output, :tenants, :sql_base_class
7
- attr_writer :context_class
6
+ # Value object for storing configuration settings
7
+ Settings = Struct.new(:pretty_output, :tenants, :context_class, :sql_base_class, :show_dashboard)
8
8
 
9
9
  def initialize
10
- @pretty_output = true
11
- @tenants = nil
12
- @context_class = nil
13
- @sql_base_class = 'ApplicationRecord'
10
+ @settings = Settings.new(true, nil, nil, 'ApplicationRecord', false)
11
+ end
12
+
13
+ %i[pretty_output tenants context_class sql_base_class show_dashboard].each do |attr|
14
+ define_method(attr) { @settings.send(attr) }
15
+ define_method("#{attr}=") { |val| @settings.send("#{attr}=", val) }
14
16
  end
15
17
 
16
18
  def context_class
17
- case @context_class
18
- when String, Symbol then resolve_context_class
19
- else @context_class
19
+ val = @settings.context_class
20
+ case val
21
+ when String, Symbol then resolve_context_class(val)
22
+ else val
20
23
  end
21
24
  end
22
25
 
@@ -28,17 +31,18 @@ module ConsoleKit
28
31
  end
29
32
 
30
33
  def validate!
31
- raise Error, 'ConsoleKit: `tenants` is not configured.' if @tenants.blank?
32
- raise Error, 'ConsoleKit: `tenants` must be a Hash.' unless @tenants.is_a?(Hash)
33
- raise Error, 'ConsoleKit: `context_class` is not configured.' if @context_class.blank?
34
+ raise Error, 'ConsoleKit: `tenants` is not configured.' if tenants.blank?
35
+ raise Error, 'ConsoleKit: `tenants` must be a Hash.' unless tenants.is_a?(Hash)
36
+ raise Error, 'ConsoleKit: `context_class` is not configured.' if @settings.context_class.blank?
34
37
  end
35
38
 
36
39
  private
37
40
 
38
- def resolve_context_class
39
- @context_class.to_s.constantize
40
- rescue NameError
41
- raise Error, "ConsoleKit: context_class '#{@context_class}' could not be found. " \
41
+ def resolve_context_class(val)
42
+ klass = val.to_s.safe_constantize
43
+ return klass if klass
44
+
45
+ raise Error, "ConsoleKit: context_class '#{val}' could not be found. " \
42
46
  'Ensure the class is defined before configuration is accessed.'
43
47
  end
44
48
  end
@@ -1,11 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/core_ext/class/subclasses'
4
+ require 'active_support/core_ext/string/inflections'
5
+ require 'active_support/core_ext/string/filters'
6
+ require_relative 'diagnostic_helpers'
4
7
 
5
8
  module ConsoleKit
6
9
  module Connections
7
10
  # Parent class for connection handlers
8
11
  class BaseConnectionHandler
12
+ include DiagnosticHelpers
13
+
9
14
  class << self
10
15
  def registry = descendants
11
16
  end
@@ -15,12 +20,41 @@ module ConsoleKit
15
20
  def initialize(context) = @context = context
16
21
  def connect = raise NotImplementedError, "#{self.class} must implement #connect"
17
22
  def available? = raise NotImplementedError, "#{self.class} must implement #available?"
23
+ def diagnostics = raise NotImplementedError, "#{self.class} must implement #diagnostics"
24
+
25
+ def safe_diagnostics(timeout: 2)
26
+ handler_name = self.class.name.demodulize.delete_suffix('ConnectionHandler')
27
+ thread, result_wrapper = spawn_diagnostic_thread(handler_name)
28
+
29
+ if thread.join(timeout)
30
+ result_wrapper[:value] || error_diagnostics(handler_name, StandardError.new('Unknown error'))
31
+ else
32
+ timeout_diagnostics(handler_name, timeout)
33
+ end
34
+ end
18
35
 
19
36
  private
20
37
 
21
- def context_attribute(name)
22
- @context.respond_to?(name, true) ? @context.send(name) : nil
38
+ def spawn_diagnostic_thread(handler_name)
39
+ wrapper = { value: nil }
40
+ thread = Thread.new { wrapper[:value] = run_diagnostics_safely(handler_name) { diagnostics } }
41
+ [thread, wrapper]
23
42
  end
43
+
44
+ def run_diagnostics_safely(name)
45
+ yield
46
+ rescue StandardError => e
47
+ error_diagnostics(name, e)
48
+ end
49
+
50
+ def measure_latency
51
+ start = clock_time
52
+ yield
53
+ ((clock_time - start) * 1000).round(1)
54
+ end
55
+
56
+ def context_attribute(name) = @context.try(name)
57
+ def unavailable_diagnostics(name) = { name: name, status: :unavailable, latency_ms: nil, details: {} }
24
58
  end
25
59
  end
26
60
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'table_renderer'
4
+
5
+ module ConsoleKit
6
+ module Connections
7
+ # Displays connection diagnostics as a Unicode table
8
+ module Dashboard
9
+ class << self
10
+ def display
11
+ rows = fetch_diagnostics
12
+ return Output.print_warning('No connections available') if rows.empty?
13
+
14
+ Output.print_header('Connection Dashboard')
15
+ Output.print_raw(TableRenderer.render(rows))
16
+ end
17
+
18
+ private
19
+
20
+ def fetch_diagnostics
21
+ ctx = ConsoleKit.configuration.context_class
22
+ handlers = ConnectionManager.available_handlers(ctx)
23
+ handlers.map(&:safe_diagnostics)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConsoleKit
4
+ module Connections
5
+ # Shared helper methods for connection diagnostics
6
+ module DiagnosticHelpers
7
+ module_function
8
+
9
+ def clock_time
10
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
11
+ end
12
+
13
+ def error_diagnostics(name, error)
14
+ { name: name, status: :error, latency_ms: nil, details: { error: error.message.truncate(60) } }
15
+ end
16
+
17
+ def timeout_diagnostics(name, timeout)
18
+ { name: name, status: :timeout, latency_ms: nil, details: { error: "Timed out after #{timeout}s" } }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -6,21 +6,64 @@ module ConsoleKit
6
6
  module Connections
7
7
  # Handles Elasticsearch connections
8
8
  class ElasticsearchConnectionHandler < BaseConnectionHandler
9
+ class << self
10
+ def elasticsearch_available?
11
+ return false unless defined?(Elasticsearch::Model)
12
+
13
+ Elasticsearch::Model.method(:client)
14
+ true
15
+ rescue NameError
16
+ false
17
+ end
18
+
19
+ def apply_prefix(prefix)
20
+ return unless defined?(Elasticsearch::Model)
21
+
22
+ Elasticsearch::Model.try(:index_name_prefix=, prefix)
23
+ end
24
+ end
25
+
9
26
  def connect
10
27
  prefix = context_attribute(:tenant_elasticsearch_prefix).presence
11
28
  Output.print_info(switch_message(prefix))
12
29
  Thread.current[:console_kit_elasticsearch_prefix] = prefix
13
- apply_model_index_prefix(prefix)
30
+ self.class.apply_prefix(prefix)
14
31
  end
15
32
 
16
- def available? = defined?(Elasticsearch)
33
+ def available? = self.class.elasticsearch_available?
34
+
35
+ def diagnostics
36
+ return unavailable_diagnostics('Elasticsearch') unless available?
37
+
38
+ perform_diagnostics
39
+ rescue StandardError => e
40
+ error_diagnostics('Elasticsearch', e)
41
+ end
17
42
 
18
43
  private
19
44
 
20
- def apply_model_index_prefix(prefix)
21
- return unless defined?(Elasticsearch::Model) && Elasticsearch::Model.respond_to?(:index_name_prefix=)
45
+ def perform_diagnostics
46
+ client = Elasticsearch::Model.client
47
+ latency = measure_latency do
48
+ client.ping
49
+ rescue StandardError
50
+ nil
51
+ end
52
+ health = client.cluster.health
53
+ build_elasticsearch_diagnostics(health['cluster_name'], health['status'], latency)
54
+ end
22
55
 
23
- Elasticsearch::Model.index_name_prefix = prefix
56
+ def build_elasticsearch_diagnostics(cluster, status, latency)
57
+ {
58
+ name: 'Elasticsearch',
59
+ status: :connected,
60
+ latency_ms: latency,
61
+ details: {
62
+ prefix: context_attribute(:tenant_elasticsearch_prefix),
63
+ cluster: cluster,
64
+ health: status
65
+ }
66
+ }
24
67
  end
25
68
 
26
69
  def switch_message(prefix)
@@ -8,18 +8,67 @@ module ConsoleKit
8
8
  class MongoConnectionHandler < BaseConnectionHandler
9
9
  def connect
10
10
  db = context_attribute(:tenant_mongo_db).presence
11
- Output.print_info(switch_message(db))
12
- Mongoid.override_database(db)
11
+ switch_mongo(db)
13
12
  rescue NoMethodError
14
- Output.print_warning('Mongoid.override_database is not available in this version of Mongoid.')
13
+ Output.print_warning('Mongoid client override is not available in this version of Mongoid.')
15
14
  end
16
15
 
17
16
  def available? = defined?(Mongoid)
18
17
 
18
+ def diagnostics
19
+ return unavailable_diagnostics('MongoDB') unless available?
20
+
21
+ perform_diagnostics
22
+ rescue StandardError => e
23
+ error_diagnostics('MongoDB', e)
24
+ end
25
+
19
26
  private
20
27
 
21
- def switch_message(db)
22
- db ? "Switching to MongoDB client: #{db}" : 'Resetting MongoDB client to default'
28
+ def perform_diagnostics
29
+ db = tenant_database
30
+ latency = measure_latency { db.command(ping: 1) }
31
+ info = db.command(buildInfo: 1).first
32
+ build_mongo_diagnostics(db.name, info['version'], latency)
33
+ end
34
+
35
+ def build_mongo_diagnostics(name, version, latency)
36
+ {
37
+ name: 'MongoDB',
38
+ status: :connected,
39
+ latency_ms: latency,
40
+ details: { database: name, version: version }
41
+ }
42
+ end
43
+
44
+ def tenant_database
45
+ override = context_attribute(:tenant_mongo_db).presence
46
+ client = Mongoid.default_client
47
+ (override ? client.use(override) : client).database
48
+ end
49
+
50
+ def switch_mongo(db)
51
+ if db.nil?
52
+ Output.print_info('Resetting MongoDB client to default')
53
+ reset_overrides
54
+ elsif named_client?(db)
55
+ Output.print_info("Switching to MongoDB client: #{db}")
56
+ Mongoid.override_client(db)
57
+ else
58
+ Output.print_info("Switching to MongoDB database: #{db}")
59
+ Mongoid.override_database(db)
60
+ end
61
+ end
62
+
63
+ def reset_overrides
64
+ Mongoid.override_client(nil) if Mongoid.respond_to?(:override_client)
65
+ Mongoid.override_database(nil)
66
+ end
67
+
68
+ def named_client?(name)
69
+ Mongoid::Config.clients.key?(name.to_s)
70
+ rescue StandardError
71
+ false
23
72
  end
24
73
  end
25
74
  end
@@ -8,29 +8,67 @@ module ConsoleKit
8
8
  class RedisConnectionHandler < BaseConnectionHandler
9
9
  DEFAULT_REDIS_DB = 0
10
10
 
11
+ class << self
12
+ def redis_client = Redis.try(:current)
13
+
14
+ def warn_no_auto_select(db_index)
15
+ Output.print_warning("Redis DB #{db_index} configured but auto-select not supported with RedisClient. " \
16
+ 'Ensure your Redis configuration sets the correct DB.')
17
+ end
18
+ end
19
+
11
20
  def connect
12
- db = context_attribute(:tenant_redis_db)
13
- Output.print_info(switch_message(db))
14
- select_redis_db(db.nil? ? DEFAULT_REDIS_DB : db)
21
+ db_index = context_attribute(:tenant_redis_db) || DEFAULT_REDIS_DB
22
+ Output.print_info(switch_message(db_index))
23
+ select_redis_db(db_index)
15
24
  end
16
25
 
17
26
  def available? = defined?(Redis)
18
27
 
28
+ def diagnostics
29
+ redis = self.class.redis_client if available?
30
+ return unavailable_diagnostics('Redis') unless redis
31
+
32
+ perform_diagnostics(redis)
33
+ rescue StandardError => e
34
+ error_diagnostics('Redis', e)
35
+ end
36
+
19
37
  private
20
38
 
21
- def select_redis_db(db)
22
- if Redis.respond_to?(:current) && Redis.current
23
- Redis.current.select(db)
24
- elsif defined?(RedisClient) && db != DEFAULT_REDIS_DB
25
- Output.print_warning("Redis DB #{db} configured but auto-select not supported with RedisClient. " \
26
- 'Ensure your Redis configuration sets the correct DB.')
39
+ def perform_diagnostics(redis)
40
+ latency = measure_latency { redis.ping }
41
+ info = redis.info
42
+ build_redis_diagnostics(info['redis_version'], info['used_memory_human'], latency)
43
+ end
44
+
45
+ def build_redis_diagnostics(version, memory, latency)
46
+ {
47
+ name: 'Redis',
48
+ status: :connected,
49
+ latency_ms: latency,
50
+ details: {
51
+ db: context_attribute(:tenant_redis_db) || DEFAULT_REDIS_DB,
52
+ version: version,
53
+ memory: memory
54
+ }
55
+ }
56
+ end
57
+
58
+ def select_redis_db(db_index)
59
+ klass = self.class
60
+ redis = klass.redis_client
61
+ if redis
62
+ redis.select(db_index)
63
+ elsif defined?(RedisClient) && db_index != DEFAULT_REDIS_DB
64
+ klass.warn_no_auto_select(db_index)
27
65
  end
28
66
  rescue NoMethodError
29
67
  Output.print_warning('Redis.current is not available (deprecated in Redis v5+).')
30
68
  end
31
69
 
32
- def switch_message(db)
33
- db ? "Switching to Redis DB: #{db}" : 'Resetting Redis connection to default'
70
+ def switch_message(db_index)
71
+ db_index ? "Switching to Redis DB: #{db_index}" : 'Resetting Redis connection to default'
34
72
  end
35
73
  end
36
74
  end
@@ -6,28 +6,70 @@ module ConsoleKit
6
6
  module Connections
7
7
  # Handles SQL connections
8
8
  class SqlConnectionHandler < BaseConnectionHandler
9
+ class << self
10
+ def sql_version(conn)
11
+ conn.select_value('SELECT version()')
12
+ rescue StandardError
13
+ nil
14
+ end
15
+
16
+ def base_class_name = ConsoleKit.configuration.sql_base_class
17
+ end
18
+
9
19
  def connect
10
20
  shard = context_attribute(:tenant_shard).presence&.to_sym
11
21
  Output.print_info("#{connection_message(shard)} via #{base_class}")
22
+ disconnect_existing_pool
12
23
  shard ? base_class.establish_connection(shard) : base_class.establish_connection
13
24
  end
14
25
 
15
- def available? = sql_base_class_name.to_s.safe_constantize.present?
26
+ def available? = self.class.base_class_name.to_s.safe_constantize.present?
27
+
28
+ def diagnostics
29
+ return unavailable_diagnostics('SQL') unless available?
30
+
31
+ perform_diagnostics
32
+ rescue StandardError => e
33
+ error_diagnostics('SQL', e)
34
+ end
16
35
 
17
36
  private
18
37
 
19
- def base_class
20
- klass = sql_base_class_name.to_s.safe_constantize
21
- return klass if klass
38
+ def perform_diagnostics
39
+ conn = base_class.connection
40
+ latency = measure_latency { conn.execute('SELECT 1') }
41
+ build_sql_diagnostics(conn, latency)
42
+ end
43
+
44
+ def disconnect_existing_pool
45
+ pool = base_class.try(:connection_pool)
46
+ pool&.disconnect!
47
+ end
22
48
 
23
- raise Error, "ConsoleKit: sql_base_class '#{sql_base_class_name}' could not be found."
49
+ def build_sql_diagnostics(conn, latency)
50
+ {
51
+ name: 'SQL',
52
+ status: :connected,
53
+ latency_ms: latency,
54
+ details: {
55
+ adapter: conn.adapter_name,
56
+ pool_size: base_class.connection_pool.size,
57
+ version: self.class.sql_version(conn).to_s.truncate(50)
58
+ }
59
+ }
60
+ end
61
+
62
+ def base_class
63
+ @base_class ||= begin
64
+ name = self.class.base_class_name
65
+ klass = name.to_s.safe_constantize
66
+ klass || raise(Error, "ConsoleKit: sql_base_class '#{name}' could not be found.")
67
+ end
24
68
  end
25
69
 
26
70
  def connection_message(shard)
27
71
  shard ? "Establishing SQL connection to shard: #{shard}" : 'Resetting SQL connection to default'
28
72
  end
29
-
30
- def sql_base_class_name = ConsoleKit.configuration.sql_base_class
31
73
  end
32
74
  end
33
75
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConsoleKit
4
+ module Connections
5
+ # Formats raw diagnostic data for table display
6
+ module TableFormatter
7
+ class << self
8
+ def format_row(diag)
9
+ [
10
+ diag[:name],
11
+ format_status(diag[:status]),
12
+ format_latency(diag[:latency_ms]),
13
+ format_details(diag[:details])
14
+ ]
15
+ end
16
+
17
+ def format_status(status)
18
+ return "\u2713 Connected" if status == :connected
19
+ return "\u2717 Error" if %i[error timeout].include?(status)
20
+ return "\u2014 N/A" if status == :unavailable
21
+
22
+ '? Unknown'
23
+ end
24
+
25
+ def format_latency(latency_ms)
26
+ latency_ms ? "#{latency_ms}ms" : "\u2014"
27
+ end
28
+
29
+ def format_details(details)
30
+ return '' unless details&.any?
31
+
32
+ details.compact.map { |key, value| "#{key}: #{value}" }.join(', ')
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'table_formatter'
4
+
5
+ module ConsoleKit
6
+ module Connections
7
+ # Renders diagnostic data into a Unicode box-drawing table
8
+ module TableRenderer
9
+ class << self
10
+ def render(rows)
11
+ headers = %w[Service Status Latency Details]
12
+ table_rows = rows.map { |row| TableFormatter.format_row(row) }
13
+ widths = calculate_widths(headers, table_rows)
14
+
15
+ build_table(headers, table_rows, widths)
16
+ end
17
+
18
+ private
19
+
20
+ def calculate_widths(headers, rows)
21
+ all_rows = [headers] + rows
22
+ headers.each_index.map do |index|
23
+ column_max_width(all_rows, index)
24
+ end
25
+ end
26
+
27
+ def column_max_width(rows, index)
28
+ rows.map { |row| row[index].length }.max
29
+ end
30
+
31
+ def build_table(headers, rows, widths)
32
+ lines = [table_top(widths), table_line(headers, widths), table_mid(widths)]
33
+ rows.each { |row| lines << table_line(row, widths) }
34
+ lines << table_bottom(widths)
35
+ lines.join("\n")
36
+ end
37
+
38
+ def table_top(widths) = "\u250C#{widths.map { |width| "\u2500" * (width + 2) }.join("\u252C")}\u2510"
39
+ def table_mid(widths) = "\u251C#{widths.map { |width| "\u2500" * (width + 2) }.join("\u253C")}\u2524"
40
+ def table_bottom(widths) = "\u2514#{widths.map { |width| "\u2500" * (width + 2) }.join("\u2534")}\u2518"
41
+
42
+ def table_line(cells, widths)
43
+ content = cells.each_with_index.map { |cell, index| " #{cell.ljust(widths[index])} " }.join("\u2502")
44
+ "\u2502#{content}\u2502"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,36 +3,62 @@
3
3
  module ConsoleKit
4
4
  # Helper methods available in the Rails console
5
5
  module ConsoleHelpers
6
- def switch_tenant = ConsoleKit.reset_current_tenant
6
+ def switch_tenant
7
+ ConsoleKit.reset_current_tenant
8
+ self
9
+ end
7
10
 
8
11
  def tenant_info
9
12
  tenant = ConsoleKit::Setup.current_tenant
10
- unless tenant
11
- ConsoleKit::Output.print_warning('No tenant is currently configured.')
12
- return
13
- end
13
+ return no_tenant_warning unless tenant
14
14
 
15
- constants = ConsoleKit.configuration.tenants[tenant]&.[](:constants) || {}
16
- print_tenant_details(tenant, constants)
15
+ display_tenant_info(tenant)
16
+ nil
17
+ end
18
+
19
+ def dashboard
20
+ ConsoleKit::Connections::Dashboard.display
21
+ self
17
22
  end
18
23
 
19
24
  def tenants
20
25
  names = ConsoleKit.configuration.tenants&.keys || []
21
- ConsoleKit::Output.print_list(names, header: 'Available Tenants')
26
+ print_available_tenants(names)
22
27
  names
23
28
  end
24
29
 
30
+ DETAIL_LABELS = {
31
+ 'Partner' => :partner_code, 'Shard' => :shard, 'Mongo DB' => :mongo_db,
32
+ 'Redis DB' => :redis_db, 'ES Prefix' => :elasticsearch_prefix, 'Environment' => :environment
33
+ }.freeze
34
+
25
35
  private
26
36
 
27
- def print_tenant_details(tenant, constants)
28
- ConsoleKit::Output.print_header("Tenant: #{tenant}")
29
- {
30
- 'Partner' => :partner_code, 'Shard' => :shard, 'Mongo DB' => :mongo_db,
31
- 'Redis DB' => :redis_db, 'ES Prefix' => :elasticsearch_prefix, 'Environment' => :environment
32
- }.each do |label, key|
33
- ConsoleKit::Output.print_info(" #{label.ljust(13)}#{constants[key]}") unless constants[key].nil?
37
+ def no_tenant_warning
38
+ ConsoleKit::Output.print_warning('No tenant is currently configured.')
39
+ self
40
+ end
41
+
42
+ def display_tenant_info(tenant)
43
+ constants = ConsoleKit.configuration.tenants[tenant]&.[](:constants) || {}
44
+ ConsoleHelpers.print_tenant_details(tenant, constants)
45
+ self
46
+ end
47
+
48
+ def print_available_tenants(names)
49
+ ConsoleKit::Output.print_list(names, header: 'Available Tenants')
50
+ self
51
+ end
52
+
53
+ class << self
54
+ def print_tenant_details(tenant, constants)
55
+ ConsoleKit::Output.print_header("Tenant: #{tenant}")
56
+ DETAIL_LABELS.each do |label, key|
57
+ next unless constants.key?(key)
58
+
59
+ ConsoleKit::Output.print_info(" #{label.ljust(13)}#{constants[key]}")
60
+ end
34
61
  end
35
- nil
36
62
  end
37
63
  end
38
64
  end