console_kit 0.1.2 ā 0.1.4
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/.reek.yml +4 -0
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +20 -0
- data/README.md +37 -1
- data/SECURITY.md +28 -0
- data/lib/console_kit/configuration.rb +18 -0
- data/lib/console_kit/connections/base_connection_handler.rb +18 -0
- data/lib/console_kit/connections/connection_manager.rb +24 -0
- data/lib/console_kit/connections/mongo_connection_handler.rb +26 -0
- data/lib/console_kit/connections/sql_connection_handler.rb +24 -0
- data/lib/console_kit/output.rb +31 -21
- data/lib/console_kit/railtie.rb +2 -4
- data/lib/console_kit/setup.rb +48 -30
- data/lib/console_kit/tenant_configurator.rb +51 -16
- data/lib/console_kit/tenant_selector.rb +39 -32
- data/lib/console_kit/version.rb +1 -1
- data/lib/console_kit.rb +21 -1
- data/lib/generators/console_kit/install_generator.rb +11 -5
- data/lib/generators/console_kit/templates/console_kit.rb +26 -8
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 634e983d59fba9a10d608945d11f0be809c4b3008612c804489a0ee18fd8bcf0
|
|
4
|
+
data.tar.gz: 646bf39efa3b81058524433d88852906313c20e0b584b1e205386bae45b8e638
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70f8f1282f3e6a63588187a86de442b663f17efd40773f7ad6c2916a21917549917d5fa9fba688774e18608f173e8126cee711cb468faf94efdda1ef7653151d
|
|
7
|
+
data.tar.gz: 436bfcb527f71341dd2f6fe1b865a884b4e4208a4dec75820e2bc46068de85db706f2e037e2b1aa94f07091600e204c35ef89cff612dd2bb103ae6d1baa414f7
|
data/.reek.yml
ADDED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,24 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.1.4] - 2025-09-30
|
|
10
|
+
### Added
|
|
11
|
+
- Minor Fixes and Improvements
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## [0.1.3] - 2025-08-12
|
|
16
|
+
### Added
|
|
17
|
+
- `ConsoleKit.current_tenant` method to retrieve the current tenant at runtime.
|
|
18
|
+
- `ConsoleKit.reset_current_tenant` to reset tenant selection.
|
|
19
|
+
- `pretty_output` configuration added with ability to manually toggle CLI verbosity.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Refactored internal logic for improved maintainability and future extensibility.
|
|
23
|
+
- Enhanced test coverage for better reliability and edge case handling.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
9
27
|
## [0.1.2] - 2025-07-23
|
|
10
28
|
### Added
|
|
11
29
|
- Changelog added.
|
|
@@ -38,6 +56,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
38
56
|
|
|
39
57
|
## [Unreleased]
|
|
40
58
|
|
|
59
|
+
[0.1.4]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.4
|
|
60
|
+
[0.1.3]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.3
|
|
41
61
|
[0.1.2]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.2
|
|
42
62
|
[0.1.1]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.1
|
|
43
63
|
[0.1.0]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# ConsoleKit
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
3
9
|
A simple and flexible multi-tenant console setup toolkit for Rails applications.
|
|
4
10
|
|
|
5
11
|
ConsoleKit helps you manage tenant-specific database connections and context configuration via an easy CLI interface and Rails integration.
|
|
@@ -52,9 +58,39 @@ ConsoleKit.configure do |config|
|
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
config.context_class = CurrentContext
|
|
61
|
+
|
|
62
|
+
# Optional: Toggle pretty CLI output
|
|
63
|
+
config.pretty_output = true
|
|
55
64
|
end
|
|
56
65
|
```
|
|
57
66
|
|
|
67
|
+
## Console Usage
|
|
68
|
+
|
|
69
|
+
When launching the Rails console, ConsoleKit will prompt you to select a tenant (if tenants are configured).
|
|
70
|
+
You can also manually interact with it:
|
|
71
|
+
|
|
72
|
+
### Get Current Tenant
|
|
73
|
+
```ruby
|
|
74
|
+
ConsoleKit.current_tenant
|
|
75
|
+
# => :tenant_one
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Reset Current Tenant
|
|
79
|
+
```ruby
|
|
80
|
+
ConsoleKit.reset_current_tenant
|
|
81
|
+
# => nil
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Manually Disable Pretty Output
|
|
85
|
+
```ruby
|
|
86
|
+
ConsoleKit.enable_pretty_output
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Manually Disable Pretty Output
|
|
90
|
+
```ruby
|
|
91
|
+
ConsoleKit.disable_pretty_output
|
|
92
|
+
```
|
|
93
|
+
|
|
58
94
|
## Development
|
|
59
95
|
|
|
60
96
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
@@ -63,7 +99,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
|
63
99
|
|
|
64
100
|
## Contributing
|
|
65
101
|
|
|
66
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/Soumyadeep-ai/console_kit. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/Soumyadeep-ai/console_kit/blob/main/CODE_OF_CONDUCT.md).
|
|
102
|
+
Bug reports and pull requests are welcome on GitHub at [Console Kit](https://github.com/Soumyadeep-ai/console_kit). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/Soumyadeep-ai/console_kit/blob/main/CODE_OF_CONDUCT.md).
|
|
67
103
|
|
|
68
104
|
## License
|
|
69
105
|
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We do **not maintain multiple supported versions**. Only the latest released version is eligible for security updates.
|
|
6
|
+
|
|
7
|
+
Once a new version is released, the previous version is branched and locked, and will no longer receive updates ā including security patches.
|
|
8
|
+
|
|
9
|
+
| Version | Supported |
|
|
10
|
+
| ------- | ------------------ |
|
|
11
|
+
| 0.1.4 | :white_check_mark: |
|
|
12
|
+
| 0.1.3 | :x: |
|
|
13
|
+
| 0.1.2 | :x: |
|
|
14
|
+
| 0.1.1 | :x: |
|
|
15
|
+
| 0.1.0 | :x: |
|
|
16
|
+
|
|
17
|
+
## Reporting a Vulnerability
|
|
18
|
+
|
|
19
|
+
If you discover a security vulnerability, please use the GitHub [Security Advisories](https://github.com/Soumyadeep-ai/console_kit/security/advisories/new) feature to report it privately.
|
|
20
|
+
|
|
21
|
+
We follow this process:
|
|
22
|
+
- All reported vulnerabilities are reviewed promptly.
|
|
23
|
+
- A new version with security fixes will be released.
|
|
24
|
+
- Once validated, we will publish an advisory and issue a patched release if necessary.
|
|
25
|
+
|
|
26
|
+
Please avoid disclosing vulnerabilities publicly until weāve had a chance to review and respond.
|
|
27
|
+
|
|
28
|
+
For more details, visit [GitHub Docs: Reporting a Vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleKit
|
|
4
|
+
# Stores ConsoleKit configurations such as tenant map and context behavior
|
|
5
|
+
class Configuration
|
|
6
|
+
attr_reader :pretty_output, :tenants, :context_class
|
|
7
|
+
|
|
8
|
+
def initialize(tenants: nil, context_class: nil)
|
|
9
|
+
@pretty_output = true
|
|
10
|
+
@tenants = tenants
|
|
11
|
+
@context_class = context_class
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
%i[pretty_output tenants context_class].each do |attr|
|
|
15
|
+
define_method("#{attr}=") { |value| instance_variable_set("@#{attr}", value) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleKit
|
|
4
|
+
module Connections
|
|
5
|
+
# Parent class for connection handlers
|
|
6
|
+
class BaseConnectionHandler
|
|
7
|
+
attr_reader :context
|
|
8
|
+
|
|
9
|
+
def initialize(context) = @context = context
|
|
10
|
+
|
|
11
|
+
def connect
|
|
12
|
+
raise NotImplementedError, "#{self.class} must implement #connect"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def available? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'sql_connection_handler'
|
|
4
|
+
require_relative 'mongo_connection_handler'
|
|
5
|
+
|
|
6
|
+
module ConsoleKit
|
|
7
|
+
module Connections
|
|
8
|
+
# Manages available connection handlers
|
|
9
|
+
class ConnectionManager
|
|
10
|
+
class << self
|
|
11
|
+
def available_handlers(context)
|
|
12
|
+
handler_classes.filter_map do |klass|
|
|
13
|
+
handler = klass.new(context)
|
|
14
|
+
handler if handler.available?
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def handler_classes = BaseConnectionHandler.descendants
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require_relative 'base_connection_handler'
|
|
5
|
+
|
|
6
|
+
module ConsoleKit
|
|
7
|
+
module Connections
|
|
8
|
+
# Handles MongoDB connections
|
|
9
|
+
class MongoConnectionHandler < BaseConnectionHandler
|
|
10
|
+
extend Forwardable
|
|
11
|
+
|
|
12
|
+
def_delegator :@context, :tenant_mongo_db
|
|
13
|
+
|
|
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)
|
|
19
|
+
rescue NoMethodError
|
|
20
|
+
Output.print_warning('Mongoid.override_client is not defined.')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def available? = defined?(Mongoid)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require_relative 'base_connection_handler'
|
|
5
|
+
|
|
6
|
+
module ConsoleKit
|
|
7
|
+
module Connections
|
|
8
|
+
# Handles SQL connections
|
|
9
|
+
class SqlConnectionHandler < BaseConnectionHandler
|
|
10
|
+
extend Forwardable
|
|
11
|
+
|
|
12
|
+
def_delegator :@context, :tenant_shard
|
|
13
|
+
|
|
14
|
+
def connect
|
|
15
|
+
return if tenant_shard.blank?
|
|
16
|
+
|
|
17
|
+
Output.print_info("Establishing SQL connection to shard: #{tenant_shard}")
|
|
18
|
+
ApplicationRecord.establish_connection(tenant_shard.to_sym)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def available? = defined?(ApplicationRecord)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/console_kit/output.rb
CHANGED
|
@@ -3,40 +3,50 @@
|
|
|
3
3
|
module ConsoleKit
|
|
4
4
|
# Handles Console outputs
|
|
5
5
|
module Output
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
PREFIX = '[ConsoleKit]'
|
|
7
|
+
TYPES = {
|
|
8
|
+
error: { symbol: '[ā]', color: '1;31' },
|
|
9
|
+
success: { symbol: '[ā]', color: '1;32' },
|
|
10
|
+
warning: { symbol: '[!]', color: '1;33' },
|
|
11
|
+
prompt: { symbol: nil, color: '1;36' },
|
|
12
|
+
header: { symbol: nil, color: '1;34' },
|
|
13
|
+
trace: { symbol: nil, color: '0;90' },
|
|
14
|
+
info: { symbol: nil, color: nil }
|
|
15
|
+
}.freeze
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
class << self
|
|
18
|
+
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)
|
|
22
|
+
end
|
|
13
23
|
end
|
|
14
24
|
|
|
25
|
+
# Backtrace prints always with timestamp, no param
|
|
15
26
|
def print_backtrace(exception)
|
|
16
|
-
exception
|
|
27
|
+
exception&.backtrace&.each { |line| print_with(:trace, " #{line}", true) }
|
|
17
28
|
end
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
print_message("\n=== #{text} ===", '1;34') # Bold Blue
|
|
21
|
-
end
|
|
30
|
+
private
|
|
22
31
|
|
|
23
|
-
def
|
|
24
|
-
|
|
32
|
+
def print_with(type, text, timestamp)
|
|
33
|
+
meta = TYPES.fetch(type)
|
|
34
|
+
message = build_message(text, meta[:symbol], timestamp)
|
|
35
|
+
output(message, meta[:color])
|
|
25
36
|
end
|
|
26
37
|
|
|
27
|
-
def
|
|
28
|
-
|
|
38
|
+
def build_message(text, symbol, timestamp)
|
|
39
|
+
"#{PREFIX} #{timestamp_prefix(timestamp)}#{symbol_prefix(symbol)}#{text}"
|
|
29
40
|
end
|
|
30
41
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
def prefix_for(value) = value ? yield(value) : ''
|
|
43
|
+
def timestamp_prefix(timestamp) = prefix_for(timestamp) { Time.current.strftime('[%Y-%m-%d %H:%M:%S] ') }
|
|
44
|
+
def symbol_prefix(symbol) = prefix_for(symbol) { |sym| "#{sym} " }
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
def output(message, color)
|
|
47
|
+
return puts message unless ConsoleKit.configuration.pretty_output && color
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
msg = "[ConsoleKit] #{text}"
|
|
39
|
-
puts color ? "\e[#{color}m#{msg}\e[0m" : msg
|
|
49
|
+
puts "\e[#{color}m#{message}\e[0m"
|
|
40
50
|
end
|
|
41
51
|
end
|
|
42
52
|
end
|
data/lib/console_kit/railtie.rb
CHANGED
data/lib/console_kit/setup.rb
CHANGED
|
@@ -6,46 +6,64 @@ require_relative 'output'
|
|
|
6
6
|
|
|
7
7
|
# Core Logic for initial Setup
|
|
8
8
|
module ConsoleKit
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
# Does the initial setup
|
|
10
|
+
module Setup
|
|
11
|
+
class << self
|
|
12
|
+
attr_reader :current_tenant
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
def setup = run_setup
|
|
15
|
+
def tenant_setup_successful? = !@current_tenant.to_s.empty?
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
def reset_current_tenant
|
|
18
|
+
return warn_no_tenants unless tenants?
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
handle_setup_error(e)
|
|
21
|
-
end
|
|
20
|
+
warn_reset if @current_tenant
|
|
21
|
+
TenantConfigurator.clear if @current_tenant
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
@current_tenant = nil
|
|
24
|
+
setup
|
|
25
|
+
end
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
private
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
end
|
|
29
|
+
def run_setup
|
|
30
|
+
return Output.print_error('No tenants configured.') if no_tenants?
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
select_and_configure
|
|
33
|
+
rescue StandardError => e
|
|
34
|
+
handle_error(e)
|
|
35
|
+
end
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
def select_and_configure
|
|
38
|
+
key = select_tenant_key
|
|
39
|
+
return Output.print_error('No tenant selected. Loading without tenant configuration.') unless key
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
configure(key)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def configure(key)
|
|
45
|
+
TenantConfigurator.configure_tenant(key)
|
|
46
|
+
return unless TenantConfigurator.configuration_success
|
|
47
|
+
|
|
48
|
+
@current_tenant = key
|
|
49
|
+
Output.print_success("Tenant initialized: #{key}")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def tenants = ConsoleKit.configuration.tenants
|
|
53
|
+
def context_class = ConsoleKit.configuration.context_class
|
|
54
|
+
def tenants? = tenants&.any?
|
|
55
|
+
def no_tenants? = !tenants?
|
|
56
|
+
def select_tenant_key = auto_select? ? tenants.keys.first : TenantSelector.select
|
|
57
|
+
def auto_select? = single_tenant? || non_interactive?
|
|
58
|
+
def single_tenant? = tenants.size == 1
|
|
59
|
+
def non_interactive? = !$stdin.tty?
|
|
60
|
+
def warn_no_tenants = Output.print_warning('Cannot reset tenant: No tenants configured.')
|
|
61
|
+
def warn_reset = Output.print_warning("Resetting tenant: #{@current_tenant}")
|
|
45
62
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
def handle_error(error)
|
|
64
|
+
Output.print_error("Error setting up tenant: #{error.message}")
|
|
65
|
+
Output.print_backtrace(error)
|
|
66
|
+
end
|
|
49
67
|
end
|
|
50
68
|
end
|
|
51
69
|
end
|
|
@@ -1,36 +1,71 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'output'
|
|
4
|
+
require_relative 'connections/connection_manager'
|
|
4
5
|
|
|
5
6
|
module ConsoleKit
|
|
6
7
|
# For tenant configuration
|
|
7
8
|
module TenantConfigurator
|
|
8
9
|
class << self
|
|
9
|
-
|
|
10
|
-
config = tenants[key]
|
|
11
|
-
return Output.print_error("No configuration found for tenant: #{key}") unless config
|
|
10
|
+
attr_reader :configuration_success
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
def configure_tenant(key)
|
|
13
|
+
constants = ConsoleKit.configuration.tenants[key]&.[](:constants)
|
|
14
|
+
return missing_config_error(key) unless constants
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
perform_configuration(key, constants)
|
|
18
17
|
rescue StandardError => e
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
handle_error(e, key)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
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
|
+
Output.print_info('Tenant context has been cleared.')
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
private
|
|
24
30
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
def validate_constants!(constants)
|
|
32
|
+
missing = %i[shard partner_code] - constants.keys
|
|
33
|
+
raise "Tenant constants missing keys: #{missing.join(', ')}" unless missing.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def missing_config_error(key)
|
|
37
|
+
@configuration_success = false
|
|
38
|
+
Output.print_error("No configuration found for tenant: #{key}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def perform_configuration(key, constants)
|
|
42
|
+
validate_constants!(constants)
|
|
43
|
+
apply_context(constants)
|
|
44
|
+
configure_success(key)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def apply_context(constant)
|
|
48
|
+
ctx = ConsoleKit.configuration.context_class
|
|
49
|
+
ctx.tenant_shard = constant[:shard]
|
|
50
|
+
ctx.tenant_mongo_db = constant[:mongo_db]
|
|
51
|
+
ctx.partner_identifier = constant[:partner_code]
|
|
52
|
+
|
|
53
|
+
setup_connections(ctx)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def setup_connections(context)
|
|
57
|
+
ConsoleKit::Connections::ConnectionManager.available_handlers(context).each(&:connect)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def configure_success(key)
|
|
61
|
+
Output.print_success("Tenant set to: #{key}")
|
|
62
|
+
@configuration_success = true
|
|
29
63
|
end
|
|
30
64
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
65
|
+
def handle_error(error, key)
|
|
66
|
+
@configuration_success = false
|
|
67
|
+
Output.print_error("Failed to configure tenant '#{key}': #{error.message}")
|
|
68
|
+
Output.print_backtrace(error)
|
|
34
69
|
end
|
|
35
70
|
end
|
|
36
71
|
end
|
|
@@ -5,57 +5,64 @@ require_relative 'output'
|
|
|
5
5
|
module ConsoleKit
|
|
6
6
|
# For tenant selection
|
|
7
7
|
module TenantSelector
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
print_tenant_selection_menu(tenants, keys)
|
|
11
|
-
|
|
12
|
-
max_attempts = 3
|
|
13
|
-
max_attempts.times do
|
|
14
|
-
index = prompt_user_for_selection(keys.size)
|
|
15
|
-
return nil if index.zero?
|
|
16
|
-
return keys[index - 1] if index.positive?
|
|
17
|
-
end
|
|
8
|
+
RETRY_LIMIT = 3
|
|
9
|
+
DEFAULT_SELECTION = '1'
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
class << self
|
|
12
|
+
def select
|
|
13
|
+
attempt_selection(RETRY_LIMIT)
|
|
20
14
|
end
|
|
21
15
|
|
|
22
16
|
private
|
|
23
17
|
|
|
24
|
-
def
|
|
18
|
+
def attempt_selection(retries_left)
|
|
19
|
+
return nil if retries_left.zero?
|
|
20
|
+
|
|
21
|
+
print_tenant_selection_menu
|
|
22
|
+
selection = parse_user_selection
|
|
23
|
+
selection ? resolve_selection(selection) : attempt_selection(retries_left - 1)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def print_tenant_selection_menu
|
|
25
27
|
Output.print_header('Multiple tenants detected. Please choose one:')
|
|
26
28
|
Output.print_info(' 0. Load without tenant (no tenant configuration)')
|
|
27
29
|
|
|
28
|
-
keys.each_with_index do |key, index|
|
|
29
|
-
|
|
30
|
-
Output.print_info(" #{index + 1}. #{key} (partner: #{partner})")
|
|
30
|
+
ConsoleKit.tenants.keys.each_with_index do |key, index|
|
|
31
|
+
Output.print_info(" #{index + 1}. #{key} (partner: #{tenant_partner(key)})")
|
|
31
32
|
end
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
def
|
|
35
|
-
Output.print_prompt("\nEnter the number of the tenant you want (or press Enter for default '1'): ")
|
|
36
|
-
input = $stdin.gets&.chomp&.strip
|
|
37
|
-
input = '1' if input.to_s.empty?
|
|
35
|
+
def tenant_partner(key) = ConsoleKit.tenants.dig(key, :constants, :partner_code) || 'N/A'
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
def parse_user_selection
|
|
38
|
+
input = read_input_with_default
|
|
39
|
+
return handle_invalid_input('Invalid input. Please enter a number.') unless valid_integer?(input)
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
index = input.to_i
|
|
42
|
+
unless valid_selection_index?(index)
|
|
43
|
+
return handle_invalid_input("Selection must be between 0 and #{max_index}.")
|
|
44
|
+
end
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
index
|
|
45
47
|
end
|
|
46
48
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
+
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
|
|
49
55
|
end
|
|
50
56
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
def handle_invalid_input(message) = Output.print_warning(message).then { nil }
|
|
58
|
+
def valid_integer?(input) = input.match?(/\A\d+\z/)
|
|
59
|
+
def max_index = ConsoleKit.tenants.size
|
|
60
|
+
def valid_selection_index?(index) = index.between?(0, max_index)
|
|
61
|
+
|
|
62
|
+
def resolve_selection(index)
|
|
63
|
+
return nil if index.zero?
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
Output.print_warning("Selection must be between 0 and #{max_index}.")
|
|
58
|
-
-1
|
|
65
|
+
ConsoleKit.tenants.keys[index - 1]
|
|
59
66
|
end
|
|
60
67
|
end
|
|
61
68
|
end
|
data/lib/console_kit/version.rb
CHANGED
data/lib/console_kit.rb
CHANGED
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'console_kit/version'
|
|
4
|
+
require_relative 'console_kit/configuration'
|
|
4
5
|
require_relative 'console_kit/setup'
|
|
5
6
|
require_relative 'console_kit/railtie' if defined?(Rails::Railtie)
|
|
6
7
|
|
|
8
|
+
# Main module for console kit
|
|
7
9
|
module ConsoleKit
|
|
10
|
+
# Base error class for ConsoleKit-related exceptions.
|
|
8
11
|
class Error < StandardError; end
|
|
9
|
-
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def configure = yield(configuration)
|
|
15
|
+
|
|
16
|
+
def configuration = Thread.current[:console_kit_configuration] ||= Configuration.new
|
|
17
|
+
def reset_configuration! = Thread.current[:console_kit_configuration] = nil
|
|
18
|
+
|
|
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) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def current_tenant = Setup.current_tenant
|
|
25
|
+
def reset_current_tenant = Setup.reset_current_tenant
|
|
26
|
+
|
|
27
|
+
def enable_pretty_output = configuration.pretty_output = true
|
|
28
|
+
def disable_pretty_output = configuration.pretty_output = false
|
|
29
|
+
end
|
|
10
30
|
end
|
|
@@ -9,20 +9,26 @@ module ConsoleKit
|
|
|
9
9
|
class InstallGenerator < Rails::Generators::Base
|
|
10
10
|
source_root File.expand_path('templates', __dir__)
|
|
11
11
|
|
|
12
|
+
class_option :force, type: :boolean, default: false, desc: 'Overwrite existing files'
|
|
13
|
+
|
|
12
14
|
def copy_initializer
|
|
15
|
+
force = options[:force]
|
|
13
16
|
initializer_path = Rails.root.join('config', 'initializers', 'console_kit.rb')
|
|
14
17
|
|
|
15
|
-
if File.exist?(initializer_path)
|
|
16
|
-
say_status
|
|
18
|
+
if File.exist?(initializer_path) && !force
|
|
19
|
+
say_status :skipped, "Initializer already exists: #{initializer_path}", :yellow
|
|
17
20
|
else
|
|
18
|
-
template 'console_kit.rb', 'config/initializers/console_kit.rb'
|
|
19
|
-
say_status
|
|
21
|
+
template 'console_kit.rb', 'config/initializers/console_kit.rb', force: force
|
|
22
|
+
say_status :created, "Initializer generated at #{initializer_path}", :green
|
|
20
23
|
end
|
|
21
24
|
end
|
|
22
25
|
|
|
23
26
|
def remind_about_customization
|
|
24
27
|
say "\nā
Setup complete!", :green
|
|
25
|
-
say '
|
|
28
|
+
say 'š Modify `config/initializers/console_kit.rb`:', :green
|
|
29
|
+
%w[tenants context_class].each do |field|
|
|
30
|
+
say " - Set `#{field}` (required)", :green
|
|
31
|
+
end
|
|
26
32
|
end
|
|
27
33
|
end
|
|
28
34
|
end
|
|
@@ -5,15 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
Rails.application.config.after_initialize do
|
|
7
7
|
ConsoleKit.configure do |config|
|
|
8
|
-
# TODO: Set your tenants source
|
|
8
|
+
# TODO: Set your tenants source, example:
|
|
9
9
|
# {
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
10
|
+
# tenant_a: {
|
|
11
|
+
# constants: {
|
|
12
|
+
# shard: :shard_1,
|
|
13
|
+
# mongo_db: 'mongo_db_1',
|
|
14
|
+
# partner_code: 'partner_a'
|
|
15
|
+
# }
|
|
16
|
+
# },
|
|
17
|
+
# tenant_b: {
|
|
18
|
+
# constants: {
|
|
19
|
+
# shard: :shard_2,
|
|
20
|
+
# mongo_db: 'mongo_db_2',
|
|
21
|
+
# partner_code: 'partner_b'
|
|
17
22
|
# }
|
|
18
23
|
# }
|
|
19
24
|
# }
|
|
@@ -21,5 +26,18 @@ Rails.application.config.after_initialize do
|
|
|
21
26
|
|
|
22
27
|
# TODO: Set your context class (e.g., CurrentContext)
|
|
23
28
|
config.context_class = nil
|
|
29
|
+
|
|
30
|
+
# Toggle pretty output on/off (default: true)
|
|
31
|
+
config.pretty_output = true
|
|
32
|
+
|
|
33
|
+
if config.tenants.nil?
|
|
34
|
+
warn '[ConsoleKit] Warning: `tenants` is not configured. ' \
|
|
35
|
+
'Please set it in config/initializers/console_kit.rb'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if config.context_class.nil?
|
|
39
|
+
warn '[ConsoleKit] Warning: `context_class` is not configured. ' \
|
|
40
|
+
'Please set it in config/initializers/console_kit.rb'
|
|
41
|
+
end
|
|
24
42
|
end
|
|
25
43
|
end
|
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.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Soumyadeep Pal
|
|
@@ -44,6 +44,7 @@ executables: []
|
|
|
44
44
|
extensions: []
|
|
45
45
|
extra_rdoc_files: []
|
|
46
46
|
files:
|
|
47
|
+
- ".reek.yml"
|
|
47
48
|
- ".rspec"
|
|
48
49
|
- ".rubocop.yml"
|
|
49
50
|
- CHANGELOG.md
|
|
@@ -51,7 +52,13 @@ files:
|
|
|
51
52
|
- LICENSE.txt
|
|
52
53
|
- README.md
|
|
53
54
|
- Rakefile
|
|
55
|
+
- SECURITY.md
|
|
54
56
|
- lib/console_kit.rb
|
|
57
|
+
- lib/console_kit/configuration.rb
|
|
58
|
+
- lib/console_kit/connections/base_connection_handler.rb
|
|
59
|
+
- lib/console_kit/connections/connection_manager.rb
|
|
60
|
+
- lib/console_kit/connections/mongo_connection_handler.rb
|
|
61
|
+
- lib/console_kit/connections/sql_connection_handler.rb
|
|
55
62
|
- lib/console_kit/output.rb
|
|
56
63
|
- lib/console_kit/railtie.rb
|
|
57
64
|
- lib/console_kit/setup.rb
|
|
@@ -69,6 +76,7 @@ metadata:
|
|
|
69
76
|
homepage_uri: https://github.com/Soumyadeep-ai/console_kit
|
|
70
77
|
source_code_uri: https://github.com/Soumyadeep-ai/console_kit
|
|
71
78
|
changelog_uri: https://github.com/Soumyadeep-ai/console_kit/blob/main/CHANGELOG.md
|
|
79
|
+
rubygems_mfa_required: 'true'
|
|
72
80
|
rdoc_options: []
|
|
73
81
|
require_paths:
|
|
74
82
|
- lib
|