console_kit 1.1.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acd47ead1008fc3bcdc25b0d995b735f69d1e6f5a8d5f8442c67dc5ee86bb9a3
4
- data.tar.gz: 55bd88158a92c6741eab85bda17d4b61828dd101e94cd5ab02097688f8994302
3
+ metadata.gz: ee9784d78350655db23efa1080d49d5e2527e1b6530ef7a1c2772974e57a89ec
4
+ data.tar.gz: 2edbd66f104e2a2e549d11991fbbdbd1f77118227dfad56122bb58baefd4ee72
5
5
  SHA512:
6
- metadata.gz: 9d1214289e7859342493f2930557554e05ad3ccb3200e041d3ade9b2f95a710b6f111eaea9bfc40f0192e98cabd1010f7be6c71469066d2fd702a74d70a33132
7
- data.tar.gz: 4564b76383af8ab6d60c6ee79644fc78a1be8633520ffe1c74a26a69936ebbacb7db8b20dd84c28c2433f532198c46c1c140ff7d31cd323d613559624fabb713
6
+ metadata.gz: 92d30809eba5bb236130e2e472cb2ae8fc34f9ea0bd69b3a5c67334199cc34a9894666797ebe66c52489f8a2c22a403d1f65e097bfafdba89e3d3d9d5ec8d5b9
7
+ data.tar.gz: db949d61fdcff82557532b163dd1df0487e6e3fb6c3f0901eb172fa83b6adc87f70b191b59dca6405b76c90fad67c3b2c58aaff21161e441ae295e3dbdea116b
@@ -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,17 @@ 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
41
+ def resolve_context_class(val)
42
+ val.to_s.constantize
40
43
  rescue NameError
41
- raise Error, "ConsoleKit: context_class '#{@context_class}' could not be found. " \
44
+ raise Error, "ConsoleKit: context_class '#{val}' could not be found. " \
42
45
  'Ensure the class is defined before configuration is accessed.'
43
46
  end
44
47
  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,47 @@ 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
+ thread.kill
33
+ timeout_diagnostics(handler_name, timeout)
34
+ end
35
+ end
18
36
 
19
37
  private
20
38
 
39
+ def spawn_diagnostic_thread(handler_name)
40
+ wrapper = { value: nil }
41
+ thread = Thread.new { wrapper[:value] = run_diagnostics_safely(handler_name) }
42
+ [thread, wrapper]
43
+ end
44
+
45
+ def run_diagnostics_safely(name)
46
+ diagnostics
47
+ rescue StandardError => e
48
+ error_diagnostics(name, e)
49
+ end
50
+
21
51
  def context_attribute(name)
22
52
  @context.respond_to?(name, true) ? @context.send(name) : nil
23
53
  end
54
+
55
+ def measure_latency
56
+ start = clock_time
57
+ yield
58
+ ((clock_time - start) * 1000).round(1)
59
+ end
60
+
61
+ def unavailable_diagnostics(name)
62
+ { name: name, status: :unavailable, latency_ms: nil, details: {} }
63
+ end
24
64
  end
25
65
  end
26
66
  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
@@ -13,12 +13,43 @@ module ConsoleKit
13
13
  apply_model_index_prefix(prefix)
14
14
  end
15
15
 
16
- def available? = defined?(Elasticsearch)
16
+ def available?
17
+ !!(defined?(Elasticsearch::Model) && Elasticsearch::Model.respond_to?(:client))
18
+ end
19
+
20
+ def diagnostics
21
+ name = 'Elasticsearch'
22
+ return unavailable_diagnostics(name) unless available?
23
+
24
+ client = Elasticsearch::Model.client
25
+ latency = measure_latency { client.ping }
26
+ build_elasticsearch_diagnostics(client, latency)
27
+ rescue StandardError => e
28
+ error_diagnostics(name, e)
29
+ end
17
30
 
18
31
  private
19
32
 
33
+ def build_elasticsearch_diagnostics(client, latency)
34
+ {
35
+ name: 'Elasticsearch',
36
+ status: :connected,
37
+ latency_ms: latency,
38
+ details: elasticsearch_details(client.cluster.health)
39
+ }
40
+ end
41
+
42
+ def elasticsearch_details(health)
43
+ {
44
+ prefix: context_attribute(:tenant_elasticsearch_prefix),
45
+ cluster: health['cluster_name'],
46
+ health: health['status']
47
+ }
48
+ end
49
+
20
50
  def apply_model_index_prefix(prefix)
21
- return unless defined?(Elasticsearch::Model) && Elasticsearch::Model.respond_to?(:index_name_prefix=)
51
+ return unless defined?(Elasticsearch::Model)
52
+ return unless Elasticsearch::Model.respond_to?(:index_name_prefix=)
22
53
 
23
54
  Elasticsearch::Model.index_name_prefix = prefix
24
55
  end
@@ -16,8 +16,41 @@ module ConsoleKit
16
16
 
17
17
  def available? = defined?(Mongoid)
18
18
 
19
+ def diagnostics
20
+ name = 'MongoDB'
21
+ return unavailable_diagnostics(name) unless available?
22
+
23
+ db = tenant_database
24
+ latency = measure_latency { db.command(ping: 1) }
25
+ build_mongo_diagnostics(db, latency)
26
+ rescue StandardError => e
27
+ error_diagnostics(name, e)
28
+ end
29
+
19
30
  private
20
31
 
32
+ def tenant_database
33
+ override = context_attribute(:tenant_mongo_db).presence
34
+ client = Mongoid.default_client
35
+ return client.database unless override
36
+
37
+ client.use(override).database
38
+ end
39
+
40
+ def build_mongo_diagnostics(database, latency)
41
+ build_info = database.command(buildInfo: 1).first
42
+ {
43
+ name: 'MongoDB',
44
+ status: :connected,
45
+ latency_ms: latency,
46
+ details: mongo_details(database.name, build_info['version'])
47
+ }
48
+ end
49
+
50
+ def mongo_details(name, version)
51
+ { database: name, version: version }
52
+ end
53
+
21
54
  def switch_message(db)
22
55
  db ? "Switching to MongoDB client: #{db}" : 'Resetting MongoDB client to default'
23
56
  end
@@ -9,26 +9,65 @@ module ConsoleKit
9
9
  DEFAULT_REDIS_DB = 0
10
10
 
11
11
  def connect
12
- db = context_attribute(:tenant_redis_db)
12
+ db = context_attribute(:tenant_redis_db) || DEFAULT_REDIS_DB
13
13
  Output.print_info(switch_message(db))
14
- select_redis_db(db.nil? ? DEFAULT_REDIS_DB : db)
14
+ select_redis_db(db)
15
15
  end
16
16
 
17
17
  def available? = defined?(Redis)
18
18
 
19
+ def diagnostics
20
+ name = 'Redis'
21
+ return unavailable_diagnostics(name) unless available?
22
+
23
+ redis = fetch_redis_client
24
+ return unavailable_diagnostics(name) unless redis
25
+
26
+ latency = measure_latency { redis.ping }
27
+ build_redis_diagnostics(redis.info, latency)
28
+ rescue StandardError => e
29
+ error_diagnostics(name, e)
30
+ end
31
+
19
32
  private
20
33
 
34
+ def fetch_redis_client
35
+ Redis.respond_to?(:current) && Redis.current
36
+ end
37
+
38
+ def build_redis_diagnostics(info, latency)
39
+ {
40
+ name: 'Redis',
41
+ status: :connected,
42
+ latency_ms: latency,
43
+ details: redis_details(info)
44
+ }
45
+ end
46
+
47
+ def redis_details(info)
48
+ {
49
+ db: context_attribute(:tenant_redis_db) || DEFAULT_REDIS_DB,
50
+ version: info['redis_version'],
51
+ memory: info['used_memory_human']
52
+ }
53
+ end
54
+
21
55
  def select_redis_db(db)
22
- if Redis.respond_to?(:current) && Redis.current
23
- Redis.current.select(db)
56
+ redis = fetch_redis_client
57
+ if redis
58
+ redis.select(db)
24
59
  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.')
60
+ warn_about_redis_client(db)
27
61
  end
28
62
  rescue NoMethodError
29
63
  Output.print_warning('Redis.current is not available (deprecated in Redis v5+).')
30
64
  end
31
65
 
66
+ def warn_about_redis_client(db)
67
+ Output.print_warning("Redis DB #{db} configured but auto-select not supported with RedisClient. " \
68
+ 'Ensure your Redis configuration sets the correct DB.')
69
+ end
70
+
32
71
  def switch_message(db)
33
72
  db ? "Switching to Redis DB: #{db}" : 'Resetting Redis connection to default'
34
73
  end
@@ -9,13 +9,51 @@ module ConsoleKit
9
9
  def connect
10
10
  shard = context_attribute(:tenant_shard).presence&.to_sym
11
11
  Output.print_info("#{connection_message(shard)} via #{base_class}")
12
+ disconnect_existing_pool
12
13
  shard ? base_class.establish_connection(shard) : base_class.establish_connection
13
14
  end
14
15
 
15
16
  def available? = sql_base_class_name.to_s.safe_constantize.present?
16
17
 
18
+ def diagnostics
19
+ name = 'SQL'
20
+ return unavailable_diagnostics(name) unless available?
21
+
22
+ conn = base_class.connection
23
+ latency = measure_latency { conn.execute('SELECT 1') }
24
+ build_sql_diagnostics(conn, latency)
25
+ rescue StandardError => e
26
+ error_diagnostics(name, e)
27
+ end
28
+
17
29
  private
18
30
 
31
+ def disconnect_existing_pool
32
+ return unless base_class.respond_to?(:connection_pool)
33
+
34
+ pool = base_class.connection_pool
35
+ pool&.disconnect!
36
+ end
37
+
38
+ def build_sql_diagnostics(conn, latency)
39
+ {
40
+ name: 'SQL',
41
+ status: :connected,
42
+ latency_ms: latency,
43
+ details: {
44
+ adapter: conn.adapter_name,
45
+ pool_size: base_class.connection_pool.size,
46
+ version: fetch_sql_version(conn).to_s.truncate(50)
47
+ }
48
+ }
49
+ end
50
+
51
+ def fetch_sql_version(conn)
52
+ conn.select_value('SELECT version()')
53
+ rescue StandardError
54
+ nil
55
+ end
56
+
19
57
  def base_class
20
58
  klass = sql_base_class_name.to_s.safe_constantize
21
59
  return klass if klass
@@ -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
@@ -16,21 +16,30 @@ module ConsoleKit
16
16
  print_tenant_details(tenant, constants)
17
17
  end
18
18
 
19
+ def dashboard = ConsoleKit::Connections::Dashboard.display
20
+
19
21
  def tenants
20
22
  names = ConsoleKit.configuration.tenants&.keys || []
21
23
  ConsoleKit::Output.print_list(names, header: 'Available Tenants')
22
24
  names
23
25
  end
24
26
 
27
+ DETAIL_LABELS = {
28
+ 'Partner' => :partner_code, 'Shard' => :shard, 'Mongo DB' => :mongo_db,
29
+ 'Redis DB' => :redis_db, 'ES Prefix' => :elasticsearch_prefix, 'Environment' => :environment
30
+ }.freeze
31
+
25
32
  private
26
33
 
27
34
  def print_tenant_details(tenant, constants)
28
35
  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?
36
+ DETAIL_LABELS.each do |label, key|
37
+ constants.fetch(key, :missing).then do |val|
38
+ case val
39
+ when :missing then next
40
+ else ConsoleKit::Output.print_info(" #{label.ljust(13)}#{val}")
41
+ end
42
+ end
34
43
  end
35
44
  nil
36
45
  end
@@ -34,7 +34,7 @@ module ConsoleKit
34
34
  return if silent
35
35
 
36
36
  formatted = (type == :header ? "\n--- #{text} ---" : text)
37
- print_with(type, formatted, timestamp, { newline: newline })
37
+ print_with(type, formatted, timestamp: timestamp, newline: newline)
38
38
  end
39
39
  end
40
40
 
@@ -51,19 +51,24 @@ module ConsoleKit
51
51
  puts text
52
52
  end
53
53
 
54
- # Backtrace prints always with timestamp, no param
55
54
  def print_backtrace(exception)
56
55
  return if silent
57
56
 
58
- exception&.backtrace&.each { |line| print_with(:trace, " #{line}", true, { newline: true }) }
57
+ exception&.backtrace&.each do |line|
58
+ print_with(:trace, " #{line}", timestamp: true)
59
+ end
59
60
  end
60
61
 
61
62
  private
62
63
 
63
- def print_with(type, text, timestamp, opts = {})
64
+ def print_with(type, text, options = {})
65
+ opts = options.is_a?(Hash) ? options : { timestamp: options }
64
66
  meta = TYPES.fetch(type)
65
- message = build_message(text, meta[:symbol], timestamp)
66
- emit(message, meta[:color], opts.fetch(:newline, true))
67
+ message = build_message(text, meta[:symbol], opts[:timestamp])
68
+ color = meta[:color]
69
+ formatted = ConsoleKit.configuration.pretty_output && color ? "\e[#{color}m#{message}\e[0m" : message
70
+
71
+ opts.fetch(:newline, true) ? puts(formatted) : print(formatted)
67
72
  end
68
73
 
69
74
  def build_message(text, symbol, timestamp)
@@ -73,12 +78,6 @@ module ConsoleKit
73
78
  def prefix_for(value) = value ? yield(value) : ''
74
79
  def timestamp_prefix(timestamp) = prefix_for(timestamp) { Time.current.strftime('[%Y-%m-%d %H:%M:%S] ') }
75
80
  def symbol_prefix(symbol) = prefix_for(symbol) { |sym| "#{sym} " }
76
-
77
- def emit(message, color, newline)
78
- writer = newline ? :puts : :print
79
- formatted = ConsoleKit.configuration.pretty_output && color ? "\e[#{color}m#{message}\e[0m" : message
80
- send(writer, formatted)
81
- end
82
81
  end
83
82
  end
84
83
  end
@@ -18,8 +18,8 @@ module ConsoleKit
18
18
 
19
19
  def apply_irb_prompt
20
20
  conf = IRB.conf
21
- conf[:PROMPT] ||= {}
22
- conf[:PROMPT][:CONSOLE_KIT] = {
21
+ prompt = conf[:PROMPT] ||= {}
22
+ prompt[:CONSOLE_KIT] = {
23
23
  PROMPT_I: "#{tenant_label} %N(%m):%03n> ",
24
24
  PROMPT_S: "#{tenant_label} %N(%m):%03n%l ",
25
25
  PROMPT_C: "#{tenant_label} %N(%m):%03n* ",
@@ -29,14 +29,23 @@ module ConsoleKit
29
29
  end
30
30
 
31
31
  def apply_pry_prompt
32
- Pry.config.prompt = Pry::Prompt.new(
33
- 'console_kit',
34
- 'ConsoleKit tenant prompt',
35
- [
36
- proc { |obj, nest_level, _pry_instance| "#{Prompt.send(:tenant_label)} (#{obj}):#{nest_level}> " },
37
- proc { |obj, nest_level, _pry_instance| "#{Prompt.send(:tenant_label)} (#{obj}):#{nest_level}* " }
38
- ]
39
- )
32
+ procs = pry_prompt_procs(tenant_label)
33
+ Pry.config.prompt = build_pry_prompt(procs)
34
+ end
35
+
36
+ def pry_prompt_procs(label)
37
+ [
38
+ proc { |obj, nest, _| "#{label} (#{obj}):#{nest}> " },
39
+ proc { |obj, nest, _| "#{label} (#{obj}):#{nest}* " }
40
+ ]
41
+ end
42
+
43
+ def build_pry_prompt(procs)
44
+ if defined?(Pry::Prompt) && Pry::Prompt.respond_to?(:new)
45
+ Pry::Prompt.new('console_kit', 'ConsoleKit tenant prompt', procs)
46
+ else
47
+ procs
48
+ end
40
49
  end
41
50
  end
42
51
  end
@@ -6,10 +6,10 @@ module ConsoleKit
6
6
  console do
7
7
  ConsoleKit::Setup.setup
8
8
  ConsoleKit::Prompt.apply
9
- if defined?(Pry)
10
- TOPLEVEL_BINDING.receiver.extend(ConsoleKit::ConsoleHelpers)
11
- elsif defined?(IRB::ExtendCommandBundle)
9
+ if defined?(IRB::ExtendCommandBundle) && !defined?(Pry)
12
10
  IRB::ExtendCommandBundle.include(ConsoleKit::ConsoleHelpers)
11
+ else
12
+ TOPLEVEL_BINDING.receiver.extend(ConsoleKit::ConsoleHelpers)
13
13
  end
14
14
  end
15
15