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 +4 -4
- data/CHANGELOG.md +26 -4
- data/README.md +8 -2
- data/SECURITY.md +2 -1
- data/lib/console_kit/configuration.rb +33 -6
- data/lib/console_kit/connections/base_connection_handler.rb +7 -5
- data/lib/console_kit/connections/connection_manager.rb +1 -1
- data/lib/console_kit/connections/mongo_connection_handler.rb +9 -4
- data/lib/console_kit/connections/sql_connection_handler.rb +16 -4
- data/lib/console_kit/output.rb +41 -9
- data/lib/console_kit/railtie.rb +2 -0
- data/lib/console_kit/setup.rb +38 -9
- data/lib/console_kit/tenant_configurator.rb +45 -11
- data/lib/console_kit/tenant_selector.rb +39 -14
- data/lib/console_kit/version.rb +1 -1
- data/lib/console_kit.rb +27 -7
- data/lib/generators/console_kit/templates/console_kit.rb +2 -1
- metadata +1 -5
- data/.reek.yml +0 -4
- data/.rspec +0 -3
- data/.rubocop.yml +0 -11
- data/Rakefile +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8e392416a4b0f32756b184aefa7431d71ce45a733479e477f9c54f9e14affcd6
|
|
4
|
+
data.tar.gz: 3b15e7006400dc93794f84e7e14828f573639c67452c0ccb4922ece8fcab9c89
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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.
|
|
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
|
-
|
|
6
|
+
attr_accessor :pretty_output, :tenants, :sql_base_class
|
|
7
|
+
attr_writer :context_class
|
|
7
8
|
|
|
8
|
-
def initialize
|
|
9
|
+
def initialize
|
|
9
10
|
@pretty_output = true
|
|
10
|
-
@tenants =
|
|
11
|
-
@context_class =
|
|
11
|
+
@tenants = nil
|
|
12
|
+
@context_class = nil
|
|
13
|
+
@sql_base_class = 'ApplicationRecord'
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
@@ -12,15 +12,20 @@ module ConsoleKit
|
|
|
12
12
|
def_delegator :@context, :tenant_mongo_db
|
|
13
13
|
|
|
14
14
|
def connect
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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("
|
|
18
|
-
|
|
18
|
+
Output.print_info("#{msg} via #{base_class}")
|
|
19
|
+
base_class.establish_connection(shard)
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
def available? =
|
|
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
|
data/lib/console_kit/output.rb
CHANGED
|
@@ -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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
+
send(method, "\e[#{color}m#{message}\e[0m")
|
|
50
82
|
end
|
|
51
83
|
end
|
|
52
84
|
end
|
data/lib/console_kit/railtie.rb
CHANGED
data/lib/console_kit/setup.rb
CHANGED
|
@@ -9,18 +9,28 @@ module ConsoleKit
|
|
|
9
9
|
# Does the initial setup
|
|
10
10
|
module Setup
|
|
11
11
|
class << self
|
|
12
|
-
|
|
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? =
|
|
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
|
|
21
|
-
TenantConfigurator.clear if
|
|
30
|
+
warn_reset if current_tenant
|
|
31
|
+
TenantConfigurator.clear if current_tenant
|
|
22
32
|
|
|
23
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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: #{
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
+
self.configuration_success = true
|
|
63
97
|
end
|
|
64
98
|
|
|
65
99
|
def handle_error(error, key)
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
88
|
+
return :none if index.zero?
|
|
64
89
|
|
|
65
90
|
ConsoleKit.tenants.keys[index - 1]
|
|
66
91
|
end
|
data/lib/console_kit/version.rb
CHANGED
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
|
|
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
|
|
17
|
-
def reset_configuration! = Thread.current[:console_kit_configuration] = nil
|
|
33
|
+
def tenants = configuration.tenants
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
|
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
data/.rspec
DELETED
data/.rubocop.yml
DELETED