console_kit 0.1.5 → 1.0.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: 6519b1ab2c0164341a30cf828095d91fe8e3872f7456abf0041a4bc58401ec54
4
- data.tar.gz: 33df556d5e20599248763f074a0f8379171bcacddb3996dede936980a1fbe6ae
3
+ metadata.gz: 8e392416a4b0f32756b184aefa7431d71ce45a733479e477f9c54f9e14affcd6
4
+ data.tar.gz: 3b15e7006400dc93794f84e7e14828f573639c67452c0ccb4922ece8fcab9c89
5
5
  SHA512:
6
- metadata.gz: 5d5041a668f6c03adbd7cd41fd902d2a9ff420c0b3da700ae1b56e3b9d5773dfb009bce276ef970eb4e0b09db6aedf41a7404a1a9c8d8c1a8a8a98e5269235ce
7
- data.tar.gz: d6db97c81c56974ff3652dca1fceded6d917556def258670b1710aa8603ef5f9a593b5e426b74ab87b3a380c5288eb2f6688346674a3add582de1e6d09502e45
6
+ metadata.gz: c6f6b2d49d2c2e2d776af957764804dfbebb397a7fbb52449013a77bed92d1ff817672154d546a304bbc92c9c385f14e5402bee250046e8b001564169f52881d
7
+ data.tar.gz: e9c24b447c674eb693a05b9552484e293a483b547e04620e78145f9c865010c27b45f487ab16b453bce8bd9a7bb8c4c180c9464abef3cda20f7e5678fdc932bd
data/CHANGELOG.md CHANGED
@@ -6,6 +6,31 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [1.0.0] - 2026-03-01
10
+ ### Added
11
+ - **Global Configuration Persistence:** ConsoleKit settings now persist across the entire session and across multiple threads.
12
+ - **Isolated Tenant Selection:** Each thread maintains its own tenant selection for safety, while sharing the global configuration.
13
+ - **Seamless Rails Reloading:** Full support for Rails `reload!`; your selected tenant and context are now automatically preserved after code reloads.
14
+ - **Reliable Tenant Switching:** Switching or clearing tenants now correctly resets all database connections (SQL and MongoDB) to their default state.
15
+ - **Flexible Tenant Selection:** Users can now select tenants by typing their names (case-insensitive) in addition to index numbers.
16
+ - **Session Control:** Added support for `exit` or `quit` commands directly at the selection prompt to terminate the console session.
17
+ - **Safe Mode:** Added a "Skip" option (0) to load the console without any tenant configuration.
18
+ - **Improved Configuration Validation:** Enhanced startup checks to provide clearer feedback if the configuration or context class is incorrectly defined.
19
+ - **Custom SQL Base Class:** New configuration option to specify a custom base class for SQL connections.
20
+
21
+ ### Changed
22
+ - **Modernized CLI Interface:** Redesigned the tenant selection menu and prompts for a cleaner, more intuitive user experience.
23
+ - **Enhanced Error Feedback:** Improved messaging for invalid selections and missing configurations.
24
+ - **Optimized Performance:** Refactored internal discovery and configuration logic for better reliability in large applications.
25
+
26
+ ### Fixed
27
+ - Fixed a bug where tenant context was lost after running `reload!` in the Rails console.
28
+ - Fixed an issue where database connections could remain tied to a previous tenant after the context was cleared.
29
+ - Resolved all stability and code quality warnings.
30
+ - Fixed timestamp formatting in console output.
31
+
32
+ ---
33
+
9
34
  ## [0.1.5] - 2025-10-12
10
35
  ### Added
11
36
  - Minor Bug Fixes
@@ -58,10 +83,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
58
83
  - Tenant-specific database configuration.
59
84
  - Colorized console output for improved UX.
60
85
 
61
- ---
62
-
63
- ## [Unreleased]
64
-
86
+ [1.0.0]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v1.0.0
65
87
  [0.1.5]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.5
66
88
  [0.1.4]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.4
67
89
  [0.1.3]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.3
data/README.md CHANGED
@@ -66,7 +66,13 @@ end
66
66
 
67
67
  ## Console Usage
68
68
 
69
- When launching the Rails console, ConsoleKit will prompt you to select a tenant (if tenants are configured).
69
+ When launching the Rails console, ConsoleKit will prompt you to select a tenant (if multiple tenants are configured).
70
+
71
+ ### Selection Options:
72
+ - **Number or Name:** Select a tenant by its index or name (case-insensitive).
73
+ - **0 (Skip):** Load the console without any tenant configuration.
74
+ - **exit / quit:** Immediately terminate the console session.
75
+
70
76
  You can also manually interact with it:
71
77
 
72
78
  ### Get Current Tenant
@@ -81,7 +87,7 @@ ConsoleKit.reset_current_tenant
81
87
  # => nil
82
88
  ```
83
89
 
84
- ### Manually Disable Pretty Output
90
+ ### Manually Enable Pretty Output
85
91
  ```ruby
86
92
  ConsoleKit.enable_pretty_output
87
93
  ```
data/SECURITY.md CHANGED
@@ -8,7 +8,8 @@ Once a new version is released, the previous version is branched and locked, and
8
8
 
9
9
  | Version | Supported |
10
10
  | ------- | ------------------ |
11
- | 0.1.5 | :white_check_mark: |
11
+ | 1.0.0 | :white_check_mark: |
12
+ | 0.1.5 | :x: |
12
13
  | 0.1.4 | :x: |
13
14
  | 0.1.3 | :x: |
14
15
  | 0.1.2 | :x: |
@@ -3,16 +3,43 @@
3
3
  module ConsoleKit
4
4
  # Stores ConsoleKit configurations such as tenant map and context behavior
5
5
  class Configuration
6
- attr_reader :pretty_output, :tenants, :context_class
6
+ attr_accessor :pretty_output, :tenants, :sql_base_class
7
+ attr_writer :context_class
7
8
 
8
- def initialize(tenants: nil, context_class: nil)
9
+ def initialize
9
10
  @pretty_output = true
10
- @tenants = tenants
11
- @context_class = context_class
11
+ @tenants = nil
12
+ @context_class = nil
13
+ @sql_base_class = 'ApplicationRecord'
12
14
  end
13
15
 
14
- %i[pretty_output tenants context_class].each do |attr|
15
- define_method("#{attr}=") { |value| instance_variable_set("@#{attr}", value) }
16
+ def context_class
17
+ case @context_class
18
+ when String, Symbol then resolve_context_class
19
+ else @context_class
20
+ end
21
+ end
22
+
23
+ def validate
24
+ validate!
25
+ true
26
+ rescue Error
27
+ false
28
+ end
29
+
30
+ 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
+ end
35
+
36
+ private
37
+
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. " \
42
+ 'Ensure the class is defined before configuration is accessed.'
16
43
  end
17
44
  end
18
45
  end
@@ -1,17 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/class/subclasses'
4
+
3
5
  module ConsoleKit
4
6
  module Connections
5
7
  # Parent class for connection handlers
6
8
  class BaseConnectionHandler
9
+ class << self
10
+ def registry = descendants
11
+ end
12
+
7
13
  attr_reader :context
8
14
 
9
15
  def initialize(context) = @context = context
10
-
11
- def connect
12
- raise NotImplementedError, "#{self.class} must implement #connect"
13
- end
14
-
16
+ def connect = raise NotImplementedError, "#{self.class} must implement #connect"
15
17
  def available? = false
16
18
  end
17
19
  end
@@ -17,7 +17,7 @@ module ConsoleKit
17
17
 
18
18
  private
19
19
 
20
- def handler_classes = BaseConnectionHandler.descendants
20
+ def handler_classes = BaseConnectionHandler.registry
21
21
  end
22
22
  end
23
23
  end
@@ -12,15 +12,20 @@ module ConsoleKit
12
12
  def_delegator :@context, :tenant_mongo_db
13
13
 
14
14
  def connect
15
- return if tenant_mongo_db.blank?
16
-
17
- Output.print_info("Switching to MongoDB client: #{tenant_mongo_db}")
18
- Mongoid.override_client(tenant_mongo_db)
15
+ db = tenant_mongo_db.presence
16
+ Output.print_info(switch_message(db))
17
+ Mongoid.override_client(db)
19
18
  rescue NoMethodError
20
19
  Output.print_warning('Mongoid.override_client is not defined.')
21
20
  end
22
21
 
23
22
  def available? = defined?(Mongoid)
23
+
24
+ private
25
+
26
+ def switch_message(db)
27
+ db ? "Switching to MongoDB client: #{db}" : 'Resetting MongoDB client to default'
28
+ end
24
29
  end
25
30
  end
26
31
  end
@@ -12,13 +12,25 @@ module ConsoleKit
12
12
  def_delegator :@context, :tenant_shard
13
13
 
14
14
  def connect
15
- return if tenant_shard.blank?
15
+ shard = tenant_shard.presence&.to_sym
16
+ msg = shard ? "Establishing SQL connection to shard: #{shard}" : 'Resetting SQL connection to default'
16
17
 
17
- Output.print_info("Establishing SQL connection to shard: #{tenant_shard}")
18
- ApplicationRecord.establish_connection(tenant_shard.to_sym)
18
+ Output.print_info("#{msg} via #{base_class}")
19
+ base_class.establish_connection(shard)
19
20
  end
20
21
 
21
- def available? = defined?(ApplicationRecord)
22
+ def available? = sql_base_class_name.to_s.safe_constantize.present?
23
+
24
+ private
25
+
26
+ def base_class
27
+ klass = sql_base_class_name.to_s.safe_constantize
28
+ return klass if klass
29
+
30
+ raise Error, "ConsoleKit: sql_base_class '#{sql_base_class_name}' could not be found."
31
+ end
32
+
33
+ def sql_base_class_name = ConsoleKit.configuration.sql_base_class
22
34
  end
23
35
  end
24
36
  end
@@ -15,24 +15,55 @@ module ConsoleKit
15
15
  }.freeze
16
16
 
17
17
  class << self
18
+ def silent = Thread.current[:console_kit_silent]
19
+
20
+ def silent=(val)
21
+ Thread.current[:console_kit_silent] = val
22
+ end
23
+
24
+ def silence
25
+ old_silent = silent
26
+ self.silent = true
27
+ yield
28
+ ensure
29
+ self.silent = old_silent
30
+ end
31
+
18
32
  TYPES.each_key do |type|
19
- define_method("print_#{type}") do |text, timestamp: false|
20
- formatted = (type == :header ? "\n=== #{text} ===" : text)
21
- print_with(type, formatted, timestamp)
33
+ define_method("print_#{type}") do |text, timestamp: false, newline: (type != :prompt)|
34
+ return if silent
35
+
36
+ formatted = (type == :header ? "\n--- #{text} ---" : text)
37
+ print_with(type, formatted, timestamp, newline: newline)
22
38
  end
23
39
  end
24
40
 
41
+ def print_list(items, header: nil)
42
+ return if silent
43
+
44
+ print_header(header) if header
45
+ items.each { |item| puts " #{item}" }
46
+ end
47
+
48
+ def print_raw(text)
49
+ return if silent
50
+
51
+ puts text
52
+ end
53
+
25
54
  # Backtrace prints always with timestamp, no param
26
55
  def print_backtrace(exception)
27
- exception&.backtrace&.each { |line| print_with(:trace, " #{line}", true) }
56
+ return if silent
57
+
58
+ exception&.backtrace&.each { |line| print_with(:trace, " #{line}", true, newline: true) }
28
59
  end
29
60
 
30
61
  private
31
62
 
32
- def print_with(type, text, timestamp)
63
+ def print_with(type, text, timestamp, newline: true)
33
64
  meta = TYPES.fetch(type)
34
65
  message = build_message(text, meta[:symbol], timestamp)
35
- output(message, meta[:color])
66
+ output(message, meta[:color], newline: newline)
36
67
  end
37
68
 
38
69
  def build_message(text, symbol, timestamp)
@@ -43,10 +74,11 @@ module ConsoleKit
43
74
  def timestamp_prefix(timestamp) = prefix_for(timestamp) { Time.current.strftime('[%Y-%m-%d %H:%M:%S] ') }
44
75
  def symbol_prefix(symbol) = prefix_for(symbol) { |sym| "#{sym} " }
45
76
 
46
- def output(message, color)
47
- return puts message unless ConsoleKit.configuration.pretty_output && color
77
+ def output(message, color, newline: true)
78
+ method = newline ? :puts : :print
79
+ return send(method, message) unless ConsoleKit.configuration.pretty_output && color
48
80
 
49
- puts "\e[#{color}m#{message}\e[0m"
81
+ send(method, "\e[#{color}m#{message}\e[0m")
50
82
  end
51
83
  end
52
84
  end
@@ -4,5 +4,7 @@ module ConsoleKit
4
4
  # Railtie for integrating ConsoleKit with Rails console.
5
5
  class Railtie < Rails::Railtie
6
6
  console { ConsoleKit::Setup.setup }
7
+
8
+ config.to_prepare { ConsoleKit::Setup.reapply if defined?(Rails::Console) }
7
9
  end
8
10
  end
@@ -9,18 +9,28 @@ module ConsoleKit
9
9
  # Does the initial setup
10
10
  module Setup
11
11
  class << self
12
- attr_reader :current_tenant
12
+ def current_tenant = Thread.current[:console_kit_current_tenant]
13
+
14
+ def current_tenant=(val)
15
+ Thread.current[:console_kit_current_tenant] = val
16
+ end
13
17
 
14
18
  def setup = run_setup
15
- def tenant_setup_successful? = !@current_tenant.to_s.empty?
19
+ def tenant_setup_successful? = !current_tenant.to_s.empty?
20
+
21
+ def reapply
22
+ return unless tenant_setup_successful?
23
+
24
+ Output.silence { TenantConfigurator.configure_tenant(current_tenant) }
25
+ end
16
26
 
17
27
  def reset_current_tenant
18
28
  return warn_no_tenants unless tenants?
19
29
 
20
- warn_reset if @current_tenant
21
- TenantConfigurator.clear if @current_tenant
30
+ warn_reset if current_tenant
31
+ TenantConfigurator.clear if current_tenant
22
32
 
23
- @current_tenant = nil
33
+ self.current_tenant = nil
24
34
  setup
25
35
  end
26
36
 
@@ -28,7 +38,8 @@ module ConsoleKit
28
38
 
29
39
  def run_setup
30
40
  return if tenant_setup_successful?
31
- return Output.print_error('No tenants configured.') if no_tenants?
41
+
42
+ ConsoleKit.configuration.validate!
32
43
 
33
44
  select_and_configure
34
45
  rescue StandardError => e
@@ -37,16 +48,34 @@ module ConsoleKit
37
48
 
38
49
  def select_and_configure
39
50
  key = select_tenant_key
40
- return Output.print_error('No tenant selected. Loading without tenant configuration.') unless key
51
+ return handle_selection_result(key) if %i[exit abort none].include?(key) || key.blank?
41
52
 
42
53
  configure(key)
43
54
  end
44
55
 
56
+ def handle_selection_result(key)
57
+ exit_on_key(key) if key == :exit
58
+
59
+ case key
60
+ when :abort, :none
61
+ Output.print_info('No tenant selected. Loading without tenant configuration.')
62
+ when nil, ''
63
+ Output.print_error('Tenant selection failed. Loading without tenant configuration.')
64
+ end
65
+ end
66
+
67
+ def exit_on_key(key)
68
+ return unless key == :exit
69
+
70
+ Output.print_info('Exiting console...')
71
+ Kernel.exit
72
+ end
73
+
45
74
  def configure(key)
46
75
  TenantConfigurator.configure_tenant(key)
47
76
  return unless TenantConfigurator.configuration_success
48
77
 
49
- @current_tenant = key
78
+ self.current_tenant = key
50
79
  Output.print_success("Tenant initialized: #{key}")
51
80
  end
52
81
 
@@ -59,7 +88,7 @@ module ConsoleKit
59
88
  def single_tenant? = tenants.size == 1
60
89
  def non_interactive? = !$stdin.tty?
61
90
  def warn_no_tenants = Output.print_warning('Cannot reset tenant: No tenants configured.')
62
- def warn_reset = Output.print_warning("Resetting tenant: #{@current_tenant}")
91
+ def warn_reset = Output.print_warning("Resetting tenant: #{current_tenant}")
63
92
 
64
93
  def handle_error(error)
65
94
  Output.print_error("Error setting up tenant: #{error.message}")
@@ -7,7 +7,11 @@ module ConsoleKit
7
7
  # For tenant configuration
8
8
  module TenantConfigurator
9
9
  class << self
10
- attr_reader :configuration_success
10
+ def configuration_success = Thread.current[:console_kit_configuration_success]
11
+
12
+ def configuration_success=(val)
13
+ Thread.current[:console_kit_configuration_success] = val
14
+ end
11
15
 
12
16
  def configure_tenant(key)
13
17
  constants = ConsoleKit.configuration.tenants[key]&.[](:constants)
@@ -19,22 +23,34 @@ module ConsoleKit
19
23
  end
20
24
 
21
25
  def clear
22
- @configuration_success = false
23
- %i[tenant_shard tenant_mongo_db partner_identifier].each do |attr|
24
- ConsoleKit.configuration.context_class.public_send("#{attr}=", nil)
25
- end
26
+ ctx = ConsoleKit.configuration.context_class
27
+ return unless ctx
28
+
29
+ reset_tenant(ctx)
26
30
  Output.print_info('Tenant context has been cleared.')
27
31
  end
28
32
 
29
33
  private
30
34
 
35
+ def reset_tenant(ctx)
36
+ self.configuration_success = false
37
+ reset_context_attributes(ctx)
38
+ setup_connections(ctx)
39
+ end
40
+
41
+ def reset_context_attributes(ctx)
42
+ %i[tenant_shard tenant_mongo_db partner_identifier].each do |attr|
43
+ ctx.public_send("#{attr}=", nil)
44
+ end
45
+ end
46
+
31
47
  def validate_constants!(constants)
32
48
  missing = %i[shard partner_code] - constants.keys
33
- raise "Tenant constants missing keys: #{missing.join(', ')}" unless missing.empty?
49
+ raise Error, "Tenant constants missing keys: #{missing.join(', ')}" unless missing.empty?
34
50
  end
35
51
 
36
52
  def missing_config_error(key)
37
- @configuration_success = false
53
+ self.configuration_success = false
38
54
  Output.print_error("No configuration found for tenant: #{key}")
39
55
  end
40
56
 
@@ -44,13 +60,31 @@ module ConsoleKit
44
60
  configure_success(key)
45
61
  end
46
62
 
63
+ def validate_context_interface!(ctx)
64
+ missing = required_interface_methods.reject { |s| ctx.respond_to?(s) }
65
+ return if missing.empty?
66
+
67
+ raise Error, "Context class #{ctx} does not implement the required interface. " \
68
+ "Missing methods: #{missing.join(', ')}"
69
+ end
70
+
71
+ def required_interface_methods
72
+ attributes = %i[tenant_shard tenant_mongo_db partner_identifier]
73
+ attributes + attributes.map { |a| "#{a}=" }
74
+ end
75
+
47
76
  def apply_context(constant)
48
77
  ctx = ConsoleKit.configuration.context_class
78
+ validate_context_interface!(ctx)
79
+
80
+ assign_context_attributes(ctx, constant)
81
+ setup_connections(ctx)
82
+ end
83
+
84
+ def assign_context_attributes(ctx, constant)
49
85
  ctx.tenant_shard = constant[:shard]
50
86
  ctx.tenant_mongo_db = constant[:mongo_db]
51
87
  ctx.partner_identifier = constant[:partner_code]
52
-
53
- setup_connections(ctx)
54
88
  end
55
89
 
56
90
  def setup_connections(context)
@@ -59,11 +93,11 @@ module ConsoleKit
59
93
 
60
94
  def configure_success(key)
61
95
  Output.print_success("Tenant set to: #{key}")
62
- @configuration_success = true
96
+ self.configuration_success = true
63
97
  end
64
98
 
65
99
  def handle_error(error, key)
66
- @configuration_success = false
100
+ self.configuration_success = false
67
101
  Output.print_error("Failed to configure tenant '#{key}': #{error.message}")
68
102
  Output.print_backtrace(error)
69
103
  end
@@ -9,9 +9,7 @@ module ConsoleKit
9
9
  DEFAULT_SELECTION = '1'
10
10
 
11
11
  class << self
12
- def select
13
- attempt_selection(RETRY_LIMIT)
14
- end
12
+ def select = attempt_selection(RETRY_LIMIT)
15
13
 
16
14
  private
17
15
 
@@ -19,26 +17,49 @@ module ConsoleKit
19
17
  return nil if retries_left.zero?
20
18
 
21
19
  print_tenant_selection_menu
20
+ process_selection(retries_left)
21
+ end
22
+
23
+ def process_selection(retries_left)
22
24
  selection = parse_user_selection
23
- selection ? resolve_selection(selection) : attempt_selection(retries_left - 1)
25
+ return :abort if selection == :abort
26
+ return attempt_selection(retries_left - 1) unless selection
27
+
28
+ selection.is_a?(Integer) ? resolve_selection(selection) : selection
24
29
  end
25
30
 
26
31
  def print_tenant_selection_menu
27
32
  Output.print_header('Multiple tenants detected. Please choose one:')
28
- Output.print_info(' 0. Load without tenant (no tenant configuration)')
33
+
34
+ items = []
35
+ items << '0. Skip (load without tenant configuration)'
29
36
 
30
37
  ConsoleKit.tenants.keys.each_with_index do |key, index|
31
- Output.print_info(" #{index + 1}. #{key} (partner: #{tenant_partner(key)})")
38
+ items << "#{index + 1}. #{key} (partner: #{tenant_partner(key)})"
32
39
  end
40
+
41
+ Output.print_list(items)
33
42
  end
34
43
 
35
44
  def tenant_partner(key) = ConsoleKit.tenants.dig(key, :constants, :partner_code) || 'N/A'
36
45
 
37
46
  def parse_user_selection
38
47
  input = read_input_with_default
39
- return handle_invalid_input('Invalid input. Please enter a number.') unless valid_integer?(input)
48
+ return :abort if input == :abort
49
+ return :exit if %w[exit quit].include?(input.downcase)
50
+ return find_tenant_by_name(input) unless valid_integer?(input)
51
+
52
+ validate_index_range(input.to_i)
53
+ end
54
+
55
+ def find_tenant_by_name(input)
56
+ match = ConsoleKit.tenants.keys.find { |k| k.to_s.casecmp(input).zero? }
57
+ return match if match
58
+
59
+ handle_invalid_input("Invalid selection: '#{input}'. Please enter a number or tenant name.")
60
+ end
40
61
 
41
- index = input.to_i
62
+ def validate_index_range(index)
42
63
  unless valid_selection_index?(index)
43
64
  return handle_invalid_input("Selection must be between 0 and #{max_index}.")
44
65
  end
@@ -47,11 +68,15 @@ module ConsoleKit
47
68
  end
48
69
 
49
70
  def read_input_with_default
50
- prompt_message = "\nEnter the number of the tenant you want " \
51
- "(or press Enter for default '#{DEFAULT_SELECTION}'): "
52
- Output.print_prompt(prompt_message)
53
- input = $stdin.gets&.chomp&.strip
54
- input.to_s.empty? ? DEFAULT_SELECTION : input
71
+ Output.print_prompt("Selection (number or name) [#{DEFAULT_SELECTION}]: ")
72
+
73
+ raw_input = $stdin.gets
74
+ raw_input ? normalize_input(raw_input) : :abort
75
+ end
76
+
77
+ def normalize_input(raw_input)
78
+ input = raw_input.chomp.strip
79
+ input.empty? ? DEFAULT_SELECTION : input
55
80
  end
56
81
 
57
82
  def handle_invalid_input(message) = Output.print_warning(message).then { nil }
@@ -60,7 +85,7 @@ module ConsoleKit
60
85
  def valid_selection_index?(index) = index.between?(0, max_index)
61
86
 
62
87
  def resolve_selection(index)
63
- return nil if index.zero?
88
+ return :none if index.zero?
64
89
 
65
90
  ConsoleKit.tenants.keys[index - 1]
66
91
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConsoleKit
4
- VERSION = '0.1.5'
4
+ VERSION = '1.0.0'
5
5
  end
data/lib/console_kit.rb CHANGED
@@ -1,29 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/object/inclusion'
5
+ require 'active_support/core_ext/string/inflections'
6
+
3
7
  require_relative 'console_kit/version'
4
8
  require_relative 'console_kit/configuration'
5
9
  require_relative 'console_kit/setup'
6
10
  require_relative 'console_kit/railtie' if defined?(Rails::Railtie)
7
11
 
8
- # Main module for console kit
12
+ # Main module for ConsoleKit
9
13
  module ConsoleKit
10
14
  # Base error class for ConsoleKit-related exceptions.
11
15
  class Error < StandardError; end
12
16
 
13
17
  class << self
14
18
  def configure = yield(configuration)
19
+ def configuration = @configuration ||= Configuration.new
20
+
21
+ def reset_configuration!
22
+ @configuration = nil
23
+ Setup.current_tenant = nil
24
+ TenantConfigurator.configuration_success = false
25
+ end
26
+
27
+ def pretty_output = configuration.pretty_output
28
+
29
+ def pretty_output=(val)
30
+ configuration.pretty_output = val
31
+ end
15
32
 
16
- def configuration = Thread.current[:console_kit_configuration] ||= Configuration.new
17
- def reset_configuration! = Thread.current[:console_kit_configuration] = nil
33
+ def tenants = configuration.tenants
18
34
 
19
- %i[pretty_output tenants context_class].each do |name|
20
- define_method(name) { configuration.public_send(name) }
21
- define_method("#{name}=") { |val| configuration.public_send("#{name}=", val) }
35
+ def tenants=(val)
36
+ configuration.tenants = val
37
+ end
38
+
39
+ def context_class = configuration.context_class
40
+
41
+ def context_class=(val)
42
+ configuration.context_class = val
22
43
  end
23
44
 
24
45
  def current_tenant = Setup.current_tenant
25
46
  def reset_current_tenant = Setup.reset_current_tenant
26
-
27
47
  def enable_pretty_output = configuration.pretty_output = true
28
48
  def disable_pretty_output = configuration.pretty_output = false
29
49
  end
@@ -24,7 +24,8 @@ Rails.application.config.after_initialize do
24
24
  # }
25
25
  config.tenants = nil
26
26
 
27
- # TODO: Set your context class (e.g., CurrentContext)
27
+ # TODO: Set your context class (e.g., 'CurrentContext')
28
+ # Recommendation: Use a String to ensure the class is correctly re-resolved after `reload!`
28
29
  config.context_class = nil
29
30
 
30
31
  # Toggle pretty output on/off (default: true)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: console_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soumyadeep Pal
@@ -44,14 +44,10 @@ executables: []
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
- - ".reek.yml"
48
- - ".rspec"
49
- - ".rubocop.yml"
50
47
  - CHANGELOG.md
51
48
  - CODE_OF_CONDUCT.md
52
49
  - LICENSE.txt
53
50
  - README.md
54
- - Rakefile
55
51
  - SECURITY.md
56
52
  - lib/console_kit.rb
57
53
  - lib/console_kit/configuration.rb
data/.reek.yml DELETED
@@ -1,4 +0,0 @@
1
- detectors:
2
- UncommunicativeVariableName:
3
- exclude:
4
- - e
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/.rubocop.yml DELETED
@@ -1,11 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 3.1
3
- NewCops: enable
4
-
5
- plugins:
6
- - rubocop-rspec
7
- - rubocop-rake
8
-
9
- Metrics/BlockLength:
10
- Exclude:
11
- - 'spec/**/*_spec.rb'
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require 'rubocop/rake_task'
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]