console_kit 1.0.0 → 1.1.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 +17 -0
- data/README.md +64 -17
- data/SECURITY.md +2 -1
- data/lib/console_kit/connections/base_connection_handler.rb +7 -1
- data/lib/console_kit/connections/connection_manager.rb +4 -0
- data/lib/console_kit/connections/elasticsearch_connection_handler.rb +31 -0
- data/lib/console_kit/connections/mongo_connection_handler.rb +3 -8
- data/lib/console_kit/connections/redis_connection_handler.rb +37 -0
- data/lib/console_kit/connections/sql_connection_handler.rb +7 -10
- data/lib/console_kit/console_helpers.rb +38 -0
- data/lib/console_kit/output.rb +8 -9
- data/lib/console_kit/prompt.rb +43 -0
- data/lib/console_kit/railtie.rb +9 -1
- data/lib/console_kit/setup.rb +48 -10
- data/lib/console_kit/tenant_configurator.rb +29 -15
- data/lib/console_kit/tenant_selector.rb +16 -15
- data/lib/console_kit/version.rb +1 -1
- data/lib/console_kit.rb +3 -1
- data/lib/generators/console_kit/templates/console_kit.rb +8 -2
- metadata +5 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: acd47ead1008fc3bcdc25b0d995b735f69d1e6f5a8d5f8442c67dc5ee86bb9a3
|
|
4
|
+
data.tar.gz: 55bd88158a92c6741eab85bda17d4b61828dd101e94cd5ab02097688f8994302
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d1214289e7859342493f2930557554e05ad3ccb3200e041d3ade9b2f95a710b6f111eaea9bfc40f0192e98cabd1010f7be6c71469066d2fd702a74d70a33132
|
|
7
|
+
data.tar.gz: 4564b76383af8ab6d60c6ee79644fc78a1be8633520ffe1c74a26a69936ebbacb7db8b20dd84c28c2433f532198c46c1c140ff7d31cd323d613559624fabb713
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [1.1.0] - 2026-03-14
|
|
10
|
+
### Added
|
|
11
|
+
- **Redis Connection Handler:** Automatic Redis DB selection per tenant via `Redis.current.select`, with graceful fallback for Redis v5+ where `Redis.current` is deprecated.
|
|
12
|
+
- **Elasticsearch Connection Handler:** Sets a per-tenant Elasticsearch index name prefix via thread-local storage and `Elasticsearch::Model.index_name_prefix=` (when available).
|
|
13
|
+
- **Console Helpers:** New `switch_tenant`, `tenant_info`, and `tenants` methods available in the Rails console for quick tenant management.
|
|
14
|
+
- **Custom Console Prompt:** IRB and Pry prompts now display the active tenant name (e.g., `[acme] main:001>`).
|
|
15
|
+
- **Tenant Banner:** On successful tenant initialization, a banner now shows the tenant name, environment safety warnings (production in red, staging in yellow), and a summary of active connections.
|
|
16
|
+
- **Environment Safety Warnings:** Production and staging environments are flagged with color-coded warnings at tenant setup time.
|
|
17
|
+
- New tenant configuration keys: `redis_db`, `elasticsearch_prefix`, and `environment`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `TenantConfigurator` now manages `tenant_redis_db` and `tenant_elasticsearch_prefix` context attributes alongside existing ones.
|
|
21
|
+
- Generator template updated with examples for the new configuration keys.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
9
25
|
## [1.0.0] - 2026-03-01
|
|
10
26
|
### Added
|
|
11
27
|
- **Global Configuration Persistence:** ConsoleKit settings now persist across the entire session and across multiple threads.
|
|
@@ -83,6 +99,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
83
99
|
- Tenant-specific database configuration.
|
|
84
100
|
- Colorized console output for improved UX.
|
|
85
101
|
|
|
102
|
+
[1.1.0]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v1.1.0
|
|
86
103
|
[1.0.0]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v1.0.0
|
|
87
104
|
[0.1.5]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.5
|
|
88
105
|
[0.1.4]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.4
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
A simple and flexible multi-tenant console setup toolkit for Rails applications.
|
|
10
10
|
|
|
11
|
-
ConsoleKit helps you manage tenant-specific database connections and context configuration via an easy CLI interface and Rails integration.
|
|
11
|
+
ConsoleKit helps you manage tenant-specific database connections (SQL, MongoDB, Redis, Elasticsearch) and context configuration via an easy CLI interface and Rails integration.
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -50,10 +50,24 @@ Then, edit config/initializers/console_kit.rb to define your tenants and context
|
|
|
50
50
|
ConsoleKit.configure do |config|
|
|
51
51
|
config.tenants = {
|
|
52
52
|
tenant_one: {
|
|
53
|
-
constants: {
|
|
53
|
+
constants: {
|
|
54
|
+
shard: :tenant_one_db,
|
|
55
|
+
mongo_db: :tenant_one_mongo,
|
|
56
|
+
partner_code: 'partnerA',
|
|
57
|
+
redis_db: 1,
|
|
58
|
+
elasticsearch_prefix: 'tenant_one',
|
|
59
|
+
environment: 'production'
|
|
60
|
+
}
|
|
54
61
|
},
|
|
55
62
|
tenant_two: {
|
|
56
|
-
constants: {
|
|
63
|
+
constants: {
|
|
64
|
+
shard: :tenant_two_db,
|
|
65
|
+
mongo_db: :tenant_two_mongo,
|
|
66
|
+
partner_code: 'partnerB',
|
|
67
|
+
redis_db: 2,
|
|
68
|
+
elasticsearch_prefix: 'tenant_two',
|
|
69
|
+
environment: 'staging'
|
|
70
|
+
}
|
|
57
71
|
}
|
|
58
72
|
}
|
|
59
73
|
|
|
@@ -64,39 +78,72 @@ ConsoleKit.configure do |config|
|
|
|
64
78
|
end
|
|
65
79
|
```
|
|
66
80
|
|
|
81
|
+
## Supported Connections
|
|
82
|
+
|
|
83
|
+
ConsoleKit automatically detects and manages connections for:
|
|
84
|
+
|
|
85
|
+
| Connection | Gem Required | Config Key | Behavior |
|
|
86
|
+
|-----------------|-----------------|--------------------------|------------------------------------------------|
|
|
87
|
+
| SQL (ActiveRecord) | `activerecord` | `shard` | Calls `establish_connection` on your base class |
|
|
88
|
+
| MongoDB | `mongoid` | `mongo_db` | Calls `Mongoid.override_database` |
|
|
89
|
+
| Redis | `redis` | `redis_db` | Calls `Redis.current.select(db)` |
|
|
90
|
+
| Elasticsearch | `elasticsearch` | `elasticsearch_prefix` | Sets `Elasticsearch::Model.index_name_prefix=` |
|
|
91
|
+
|
|
92
|
+
Handlers are only activated when their corresponding gem is loaded.
|
|
93
|
+
|
|
67
94
|
## Console Usage
|
|
68
95
|
|
|
69
|
-
When launching the Rails console, ConsoleKit will prompt you to select a tenant (if multiple tenants are configured).
|
|
96
|
+
When launching the Rails console, ConsoleKit will prompt you to select a tenant (if multiple tenants are configured). On selection, a tenant banner is displayed showing the tenant name, environment safety warnings, and active connections.
|
|
70
97
|
|
|
71
98
|
### Selection Options:
|
|
72
99
|
- **Number or Name:** Select a tenant by its index or name (case-insensitive).
|
|
73
100
|
- **0 (Skip):** Load the console without any tenant configuration.
|
|
74
101
|
- **exit / quit:** Immediately terminate the console session.
|
|
75
102
|
|
|
76
|
-
|
|
103
|
+
### Console Helpers
|
|
77
104
|
|
|
78
|
-
|
|
79
|
-
```ruby
|
|
80
|
-
ConsoleKit.current_tenant
|
|
81
|
-
# => :tenant_one
|
|
82
|
-
```
|
|
105
|
+
The following helper methods are available in your Rails console:
|
|
83
106
|
|
|
84
|
-
### Reset Current Tenant
|
|
85
107
|
```ruby
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
# Switch to a different tenant
|
|
109
|
+
switch_tenant
|
|
110
|
+
|
|
111
|
+
# Print details about the current tenant
|
|
112
|
+
tenant_info
|
|
113
|
+
|
|
114
|
+
# List all available tenants
|
|
115
|
+
tenants
|
|
88
116
|
```
|
|
89
117
|
|
|
90
|
-
###
|
|
91
|
-
|
|
92
|
-
ConsoleKit
|
|
118
|
+
### Custom Prompt
|
|
119
|
+
|
|
120
|
+
ConsoleKit automatically sets your IRB/Pry prompt to show the active tenant:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
[tenant_one] main:001>
|
|
93
124
|
```
|
|
94
125
|
|
|
95
|
-
###
|
|
126
|
+
### Other Methods
|
|
127
|
+
|
|
96
128
|
```ruby
|
|
129
|
+
# Get current tenant
|
|
130
|
+
ConsoleKit.current_tenant
|
|
131
|
+
# => :tenant_one
|
|
132
|
+
|
|
133
|
+
# Reset and re-select tenant
|
|
134
|
+
ConsoleKit.reset_current_tenant
|
|
135
|
+
|
|
136
|
+
# Toggle pretty output
|
|
137
|
+
ConsoleKit.enable_pretty_output
|
|
97
138
|
ConsoleKit.disable_pretty_output
|
|
98
139
|
```
|
|
99
140
|
|
|
141
|
+
### Environment Warnings
|
|
142
|
+
|
|
143
|
+
When a tenant has an `environment` key in its constants:
|
|
144
|
+
- **production**: A red warning is displayed at setup time.
|
|
145
|
+
- **staging**: A yellow warning is displayed at setup time.
|
|
146
|
+
|
|
100
147
|
## Development
|
|
101
148
|
|
|
102
149
|
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.
|
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
|
-
| 1.
|
|
11
|
+
| 1.1.0 | :white_check_mark: |
|
|
12
|
+
| 1.0.0 | :x: |
|
|
12
13
|
| 0.1.5 | :x: |
|
|
13
14
|
| 0.1.4 | :x: |
|
|
14
15
|
| 0.1.3 | :x: |
|
|
@@ -14,7 +14,13 @@ module ConsoleKit
|
|
|
14
14
|
|
|
15
15
|
def initialize(context) = @context = context
|
|
16
16
|
def connect = raise NotImplementedError, "#{self.class} must implement #connect"
|
|
17
|
-
def available? =
|
|
17
|
+
def available? = raise NotImplementedError, "#{self.class} must implement #available?"
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def context_attribute(name)
|
|
22
|
+
@context.respond_to?(name, true) ? @context.send(name) : nil
|
|
23
|
+
end
|
|
18
24
|
end
|
|
19
25
|
end
|
|
20
26
|
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'sql_connection_handler'
|
|
4
4
|
require_relative 'mongo_connection_handler'
|
|
5
|
+
require_relative 'redis_connection_handler'
|
|
6
|
+
require_relative 'elasticsearch_connection_handler'
|
|
5
7
|
|
|
6
8
|
module ConsoleKit
|
|
7
9
|
module Connections
|
|
@@ -12,6 +14,8 @@ module ConsoleKit
|
|
|
12
14
|
handler_classes.filter_map do |klass|
|
|
13
15
|
handler = klass.new(context)
|
|
14
16
|
handler if handler.available?
|
|
17
|
+
rescue NotImplementedError
|
|
18
|
+
nil
|
|
15
19
|
end
|
|
16
20
|
end
|
|
17
21
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_connection_handler'
|
|
4
|
+
|
|
5
|
+
module ConsoleKit
|
|
6
|
+
module Connections
|
|
7
|
+
# Handles Elasticsearch connections
|
|
8
|
+
class ElasticsearchConnectionHandler < BaseConnectionHandler
|
|
9
|
+
def connect
|
|
10
|
+
prefix = context_attribute(:tenant_elasticsearch_prefix).presence
|
|
11
|
+
Output.print_info(switch_message(prefix))
|
|
12
|
+
Thread.current[:console_kit_elasticsearch_prefix] = prefix
|
|
13
|
+
apply_model_index_prefix(prefix)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def available? = defined?(Elasticsearch)
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def apply_model_index_prefix(prefix)
|
|
21
|
+
return unless defined?(Elasticsearch::Model) && Elasticsearch::Model.respond_to?(:index_name_prefix=)
|
|
22
|
+
|
|
23
|
+
Elasticsearch::Model.index_name_prefix = prefix
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def switch_message(prefix)
|
|
27
|
+
prefix ? "Setting Elasticsearch index prefix: #{prefix}" : 'Resetting Elasticsearch index prefix to default'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'forwardable'
|
|
4
3
|
require_relative 'base_connection_handler'
|
|
5
4
|
|
|
6
5
|
module ConsoleKit
|
|
7
6
|
module Connections
|
|
8
7
|
# Handles MongoDB connections
|
|
9
8
|
class MongoConnectionHandler < BaseConnectionHandler
|
|
10
|
-
extend Forwardable
|
|
11
|
-
|
|
12
|
-
def_delegator :@context, :tenant_mongo_db
|
|
13
|
-
|
|
14
9
|
def connect
|
|
15
|
-
db = tenant_mongo_db.presence
|
|
10
|
+
db = context_attribute(:tenant_mongo_db).presence
|
|
16
11
|
Output.print_info(switch_message(db))
|
|
17
|
-
Mongoid.
|
|
12
|
+
Mongoid.override_database(db)
|
|
18
13
|
rescue NoMethodError
|
|
19
|
-
Output.print_warning('Mongoid.
|
|
14
|
+
Output.print_warning('Mongoid.override_database is not available in this version of Mongoid.')
|
|
20
15
|
end
|
|
21
16
|
|
|
22
17
|
def available? = defined?(Mongoid)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_connection_handler'
|
|
4
|
+
|
|
5
|
+
module ConsoleKit
|
|
6
|
+
module Connections
|
|
7
|
+
# Handles Redis connections
|
|
8
|
+
class RedisConnectionHandler < BaseConnectionHandler
|
|
9
|
+
DEFAULT_REDIS_DB = 0
|
|
10
|
+
|
|
11
|
+
def connect
|
|
12
|
+
db = context_attribute(:tenant_redis_db)
|
|
13
|
+
Output.print_info(switch_message(db))
|
|
14
|
+
select_redis_db(db.nil? ? DEFAULT_REDIS_DB : db)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def available? = defined?(Redis)
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def select_redis_db(db)
|
|
22
|
+
if Redis.respond_to?(:current) && Redis.current
|
|
23
|
+
Redis.current.select(db)
|
|
24
|
+
elsif defined?(RedisClient) && db != DEFAULT_REDIS_DB
|
|
25
|
+
Output.print_warning("Redis DB #{db} configured but auto-select not supported with RedisClient. " \
|
|
26
|
+
'Ensure your Redis configuration sets the correct DB.')
|
|
27
|
+
end
|
|
28
|
+
rescue NoMethodError
|
|
29
|
+
Output.print_warning('Redis.current is not available (deprecated in Redis v5+).')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def switch_message(db)
|
|
33
|
+
db ? "Switching to Redis DB: #{db}" : 'Resetting Redis connection to default'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'forwardable'
|
|
4
3
|
require_relative 'base_connection_handler'
|
|
5
4
|
|
|
6
5
|
module ConsoleKit
|
|
7
6
|
module Connections
|
|
8
7
|
# Handles SQL connections
|
|
9
8
|
class SqlConnectionHandler < BaseConnectionHandler
|
|
10
|
-
extend Forwardable
|
|
11
|
-
|
|
12
|
-
def_delegator :@context, :tenant_shard
|
|
13
|
-
|
|
14
9
|
def connect
|
|
15
|
-
shard = tenant_shard.presence&.to_sym
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Output.print_info("#{msg} via #{base_class}")
|
|
19
|
-
base_class.establish_connection(shard)
|
|
10
|
+
shard = context_attribute(:tenant_shard).presence&.to_sym
|
|
11
|
+
Output.print_info("#{connection_message(shard)} via #{base_class}")
|
|
12
|
+
shard ? base_class.establish_connection(shard) : base_class.establish_connection
|
|
20
13
|
end
|
|
21
14
|
|
|
22
15
|
def available? = sql_base_class_name.to_s.safe_constantize.present?
|
|
@@ -30,6 +23,10 @@ module ConsoleKit
|
|
|
30
23
|
raise Error, "ConsoleKit: sql_base_class '#{sql_base_class_name}' could not be found."
|
|
31
24
|
end
|
|
32
25
|
|
|
26
|
+
def connection_message(shard)
|
|
27
|
+
shard ? "Establishing SQL connection to shard: #{shard}" : 'Resetting SQL connection to default'
|
|
28
|
+
end
|
|
29
|
+
|
|
33
30
|
def sql_base_class_name = ConsoleKit.configuration.sql_base_class
|
|
34
31
|
end
|
|
35
32
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleKit
|
|
4
|
+
# Helper methods available in the Rails console
|
|
5
|
+
module ConsoleHelpers
|
|
6
|
+
def switch_tenant = ConsoleKit.reset_current_tenant
|
|
7
|
+
|
|
8
|
+
def tenant_info
|
|
9
|
+
tenant = ConsoleKit::Setup.current_tenant
|
|
10
|
+
unless tenant
|
|
11
|
+
ConsoleKit::Output.print_warning('No tenant is currently configured.')
|
|
12
|
+
return
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
constants = ConsoleKit.configuration.tenants[tenant]&.[](:constants) || {}
|
|
16
|
+
print_tenant_details(tenant, constants)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def tenants
|
|
20
|
+
names = ConsoleKit.configuration.tenants&.keys || []
|
|
21
|
+
ConsoleKit::Output.print_list(names, header: 'Available Tenants')
|
|
22
|
+
names
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def print_tenant_details(tenant, constants)
|
|
28
|
+
ConsoleKit::Output.print_header("Tenant: #{tenant}")
|
|
29
|
+
{
|
|
30
|
+
'Partner' => :partner_code, 'Shard' => :shard, 'Mongo DB' => :mongo_db,
|
|
31
|
+
'Redis DB' => :redis_db, 'ES Prefix' => :elasticsearch_prefix, 'Environment' => :environment
|
|
32
|
+
}.each do |label, key|
|
|
33
|
+
ConsoleKit::Output.print_info(" #{label.ljust(13)}#{constants[key]}") unless constants[key].nil?
|
|
34
|
+
end
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/console_kit/output.rb
CHANGED
|
@@ -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, { newline: newline })
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -55,15 +55,15 @@ module ConsoleKit
|
|
|
55
55
|
def print_backtrace(exception)
|
|
56
56
|
return if silent
|
|
57
57
|
|
|
58
|
-
exception&.backtrace&.each { |line| print_with(:trace, " #{line}", true, newline: true) }
|
|
58
|
+
exception&.backtrace&.each { |line| print_with(:trace, " #{line}", true, { newline: true }) }
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
private
|
|
62
62
|
|
|
63
|
-
def print_with(type, text, timestamp,
|
|
63
|
+
def print_with(type, text, timestamp, opts = {})
|
|
64
64
|
meta = TYPES.fetch(type)
|
|
65
65
|
message = build_message(text, meta[:symbol], timestamp)
|
|
66
|
-
|
|
66
|
+
emit(message, meta[:color], opts.fetch(:newline, true))
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def build_message(text, symbol, timestamp)
|
|
@@ -74,11 +74,10 @@ module ConsoleKit
|
|
|
74
74
|
def timestamp_prefix(timestamp) = prefix_for(timestamp) { Time.current.strftime('[%Y-%m-%d %H:%M:%S] ') }
|
|
75
75
|
def symbol_prefix(symbol) = prefix_for(symbol) { |sym| "#{sym} " }
|
|
76
76
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
send(method, "\e[#{color}m#{message}\e[0m")
|
|
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)
|
|
82
81
|
end
|
|
83
82
|
end
|
|
84
83
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleKit
|
|
4
|
+
# Sets the console prompt to show the current tenant
|
|
5
|
+
module Prompt
|
|
6
|
+
class << self
|
|
7
|
+
def apply
|
|
8
|
+
apply_irb_prompt if defined?(IRB)
|
|
9
|
+
apply_pry_prompt if defined?(Pry)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def tenant_label
|
|
15
|
+
tenant = ConsoleKit::Setup.current_tenant
|
|
16
|
+
tenant ? "[#{tenant}]" : '[no-tenant]'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def apply_irb_prompt
|
|
20
|
+
conf = IRB.conf
|
|
21
|
+
conf[:PROMPT] ||= {}
|
|
22
|
+
conf[:PROMPT][:CONSOLE_KIT] = {
|
|
23
|
+
PROMPT_I: "#{tenant_label} %N(%m):%03n> ",
|
|
24
|
+
PROMPT_S: "#{tenant_label} %N(%m):%03n%l ",
|
|
25
|
+
PROMPT_C: "#{tenant_label} %N(%m):%03n* ",
|
|
26
|
+
RETURN: "=> %s\n"
|
|
27
|
+
}
|
|
28
|
+
conf[:PROMPT_MODE] = :CONSOLE_KIT
|
|
29
|
+
end
|
|
30
|
+
|
|
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
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/console_kit/railtie.rb
CHANGED
|
@@ -3,7 +3,15 @@
|
|
|
3
3
|
module ConsoleKit
|
|
4
4
|
# Railtie for integrating ConsoleKit with Rails console.
|
|
5
5
|
class Railtie < Rails::Railtie
|
|
6
|
-
console
|
|
6
|
+
console do
|
|
7
|
+
ConsoleKit::Setup.setup
|
|
8
|
+
ConsoleKit::Prompt.apply
|
|
9
|
+
if defined?(Pry)
|
|
10
|
+
TOPLEVEL_BINDING.receiver.extend(ConsoleKit::ConsoleHelpers)
|
|
11
|
+
elsif defined?(IRB::ExtendCommandBundle)
|
|
12
|
+
IRB::ExtendCommandBundle.include(ConsoleKit::ConsoleHelpers)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
7
15
|
|
|
8
16
|
config.to_prepare { ConsoleKit::Setup.reapply if defined?(Rails::Console) }
|
|
9
17
|
end
|
data/lib/console_kit/setup.rb
CHANGED
|
@@ -9,6 +9,11 @@ module ConsoleKit
|
|
|
9
9
|
# Does the initial setup
|
|
10
10
|
module Setup
|
|
11
11
|
class << self
|
|
12
|
+
ENVIRONMENT_WARNINGS = {
|
|
13
|
+
'production' => -> { Output.print_error('WARNING: You are connected to a PRODUCTION environment!') },
|
|
14
|
+
'staging' => -> { Output.print_warning('You are connected to a staging environment.') }
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
12
17
|
def current_tenant = Thread.current[:console_kit_current_tenant]
|
|
13
18
|
|
|
14
19
|
def current_tenant=(val)
|
|
@@ -27,11 +32,13 @@ module ConsoleKit
|
|
|
27
32
|
def reset_current_tenant
|
|
28
33
|
return warn_no_tenants unless tenants?
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
key = select_tenant_key
|
|
36
|
+
return cancel_switch if key == :abort || key.blank?
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
clear_current_tenant
|
|
39
|
+
return skip_tenant_message if %i[exit none].include?(key)
|
|
40
|
+
|
|
41
|
+
configure(key)
|
|
35
42
|
end
|
|
36
43
|
|
|
37
44
|
private
|
|
@@ -40,7 +47,6 @@ module ConsoleKit
|
|
|
40
47
|
return if tenant_setup_successful?
|
|
41
48
|
|
|
42
49
|
ConsoleKit.configuration.validate!
|
|
43
|
-
|
|
44
50
|
select_and_configure
|
|
45
51
|
rescue StandardError => e
|
|
46
52
|
handle_error(e)
|
|
@@ -54,19 +60,17 @@ module ConsoleKit
|
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def handle_selection_result(key)
|
|
57
|
-
exit_on_key
|
|
63
|
+
exit_on_key if %i[exit abort].include?(key)
|
|
58
64
|
|
|
59
65
|
case key
|
|
60
|
-
when :
|
|
66
|
+
when :none
|
|
61
67
|
Output.print_info('No tenant selected. Loading without tenant configuration.')
|
|
62
68
|
when nil, ''
|
|
63
69
|
Output.print_error('Tenant selection failed. Loading without tenant configuration.')
|
|
64
70
|
end
|
|
65
71
|
end
|
|
66
72
|
|
|
67
|
-
def exit_on_key
|
|
68
|
-
return unless key == :exit
|
|
69
|
-
|
|
73
|
+
def exit_on_key
|
|
70
74
|
Output.print_info('Exiting console...')
|
|
71
75
|
Kernel.exit
|
|
72
76
|
end
|
|
@@ -76,7 +80,31 @@ module ConsoleKit
|
|
|
76
80
|
return unless TenantConfigurator.configuration_success
|
|
77
81
|
|
|
78
82
|
self.current_tenant = key
|
|
83
|
+
Prompt.apply
|
|
84
|
+
print_tenant_banner(key)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def print_tenant_banner(key)
|
|
88
|
+
constants = ConsoleKit.configuration.tenants[key]&.[](:constants) || {}
|
|
89
|
+
env = constants[:environment]&.to_s&.downcase
|
|
79
90
|
Output.print_success("Tenant initialized: #{key}")
|
|
91
|
+
print_environment_warning(env) if env
|
|
92
|
+
print_active_connections
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def print_environment_warning(env) = ENVIRONMENT_WARNINGS[env]&.call
|
|
96
|
+
|
|
97
|
+
def print_active_connections
|
|
98
|
+
names = active_connection_names
|
|
99
|
+
Output.print_info("Active connections: #{names.join(', ')}") unless names.empty?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def active_connection_names
|
|
103
|
+
ctx = context_class
|
|
104
|
+
return [] unless ctx
|
|
105
|
+
|
|
106
|
+
handlers = ConsoleKit::Connections::ConnectionManager.available_handlers(ctx)
|
|
107
|
+
handlers.map { |h| h.class.name.demodulize.delete_suffix('ConnectionHandler') }
|
|
80
108
|
end
|
|
81
109
|
|
|
82
110
|
def tenants = ConsoleKit.configuration.tenants
|
|
@@ -89,6 +117,16 @@ module ConsoleKit
|
|
|
89
117
|
def non_interactive? = !$stdin.tty?
|
|
90
118
|
def warn_no_tenants = Output.print_warning('Cannot reset tenant: No tenants configured.')
|
|
91
119
|
def warn_reset = Output.print_warning("Resetting tenant: #{current_tenant}")
|
|
120
|
+
def cancel_switch = Output.print_warning('Tenant switch cancelled.')
|
|
121
|
+
def skip_tenant_message = Output.print_info('No tenant selected. Loading without tenant configuration.')
|
|
122
|
+
|
|
123
|
+
def clear_current_tenant
|
|
124
|
+
if current_tenant
|
|
125
|
+
warn_reset
|
|
126
|
+
TenantConfigurator.clear
|
|
127
|
+
end
|
|
128
|
+
self.current_tenant = nil
|
|
129
|
+
end
|
|
92
130
|
|
|
93
131
|
def handle_error(error)
|
|
94
132
|
Output.print_error("Error setting up tenant: #{error.message}")
|
|
@@ -7,6 +7,13 @@ module ConsoleKit
|
|
|
7
7
|
# For tenant configuration
|
|
8
8
|
module TenantConfigurator
|
|
9
9
|
class << self
|
|
10
|
+
HANDLER_ATTRIBUTES = {
|
|
11
|
+
Connections::SqlConnectionHandler => :tenant_shard,
|
|
12
|
+
Connections::MongoConnectionHandler => :tenant_mongo_db,
|
|
13
|
+
Connections::RedisConnectionHandler => :tenant_redis_db,
|
|
14
|
+
Connections::ElasticsearchConnectionHandler => :tenant_elasticsearch_prefix
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
10
17
|
def configuration_success = Thread.current[:console_kit_configuration_success]
|
|
11
18
|
|
|
12
19
|
def configuration_success=(val)
|
|
@@ -39,7 +46,7 @@ module ConsoleKit
|
|
|
39
46
|
end
|
|
40
47
|
|
|
41
48
|
def reset_context_attributes(ctx)
|
|
42
|
-
|
|
49
|
+
available_context_attributes(ctx).each do |attr|
|
|
43
50
|
ctx.public_send("#{attr}=", nil)
|
|
44
51
|
end
|
|
45
52
|
end
|
|
@@ -60,31 +67,38 @@ module ConsoleKit
|
|
|
60
67
|
configure_success(key)
|
|
61
68
|
end
|
|
62
69
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
raise Error, "Context class #{ctx} does not implement the required interface. " \
|
|
68
|
-
"Missing methods: #{missing.join(', ')}"
|
|
70
|
+
def handler_available?(handler_class)
|
|
71
|
+
handler_class.new(nil).available?
|
|
72
|
+
rescue NotImplementedError, StandardError
|
|
73
|
+
false
|
|
69
74
|
end
|
|
70
75
|
|
|
71
|
-
def
|
|
72
|
-
attributes = %i[
|
|
73
|
-
|
|
76
|
+
def available_context_attributes(ctx)
|
|
77
|
+
attributes = %i[partner_identifier]
|
|
78
|
+
HANDLER_ATTRIBUTES.each do |handler, attr|
|
|
79
|
+
attributes << attr if handler_available?(handler) && ctx.respond_to?("#{attr}=")
|
|
80
|
+
end
|
|
81
|
+
attributes.select { |attr| ctx.respond_to?("#{attr}=") }
|
|
74
82
|
end
|
|
75
83
|
|
|
76
84
|
def apply_context(constant)
|
|
77
85
|
ctx = ConsoleKit.configuration.context_class
|
|
78
|
-
validate_context_interface!(ctx)
|
|
79
|
-
|
|
80
86
|
assign_context_attributes(ctx, constant)
|
|
81
87
|
setup_connections(ctx)
|
|
82
88
|
end
|
|
83
89
|
|
|
84
90
|
def assign_context_attributes(ctx, constant)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
attribute_to_constant = {
|
|
92
|
+
partner_identifier: :partner_code,
|
|
93
|
+
tenant_shard: :shard,
|
|
94
|
+
tenant_mongo_db: :mongo_db,
|
|
95
|
+
tenant_redis_db: :redis_db,
|
|
96
|
+
tenant_elasticsearch_prefix: :elasticsearch_prefix
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
available_context_attributes(ctx).each do |attr|
|
|
100
|
+
ctx.public_send("#{attr}=", constant[attribute_to_constant[attr]])
|
|
101
|
+
end
|
|
88
102
|
end
|
|
89
103
|
|
|
90
104
|
def setup_connections(context)
|
|
@@ -9,36 +9,36 @@ module ConsoleKit
|
|
|
9
9
|
DEFAULT_SELECTION = '1'
|
|
10
10
|
|
|
11
11
|
class << self
|
|
12
|
-
def select
|
|
12
|
+
def select
|
|
13
|
+
RETRY_LIMIT.times do
|
|
14
|
+
result = attempt_selection
|
|
15
|
+
return result unless result == :retry
|
|
16
|
+
end
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
13
19
|
|
|
14
20
|
private
|
|
15
21
|
|
|
16
|
-
def attempt_selection
|
|
17
|
-
return nil if retries_left.zero?
|
|
18
|
-
|
|
22
|
+
def attempt_selection
|
|
19
23
|
print_tenant_selection_menu
|
|
20
|
-
process_selection(retries_left)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def process_selection(retries_left)
|
|
24
24
|
selection = parse_user_selection
|
|
25
25
|
return :abort if selection == :abort
|
|
26
|
-
return
|
|
26
|
+
return :retry unless selection
|
|
27
27
|
|
|
28
28
|
selection.is_a?(Integer) ? resolve_selection(selection) : selection
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def print_tenant_selection_menu
|
|
32
32
|
Output.print_header('Multiple tenants detected. Please choose one:')
|
|
33
|
+
Output.print_list(menu_items)
|
|
34
|
+
end
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
items
|
|
36
|
-
|
|
36
|
+
def menu_items
|
|
37
|
+
items = ['0. Skip (load without tenant configuration)']
|
|
37
38
|
ConsoleKit.tenants.keys.each_with_index do |key, index|
|
|
38
39
|
items << "#{index + 1}. #{key} (partner: #{tenant_partner(key)})"
|
|
39
40
|
end
|
|
40
|
-
|
|
41
|
-
Output.print_list(items)
|
|
41
|
+
items
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def tenant_partner(key) = ConsoleKit.tenants.dig(key, :constants, :partner_code) || 'N/A'
|
|
@@ -69,9 +69,10 @@ module ConsoleKit
|
|
|
69
69
|
|
|
70
70
|
def read_input_with_default
|
|
71
71
|
Output.print_prompt("Selection (number or name) [#{DEFAULT_SELECTION}]: ")
|
|
72
|
-
|
|
73
72
|
raw_input = $stdin.gets
|
|
74
73
|
raw_input ? normalize_input(raw_input) : :abort
|
|
74
|
+
rescue Interrupt
|
|
75
|
+
:abort
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def normalize_input(raw_input)
|
data/lib/console_kit/version.rb
CHANGED
data/lib/console_kit.rb
CHANGED
|
@@ -7,6 +7,8 @@ require 'active_support/core_ext/string/inflections'
|
|
|
7
7
|
require_relative 'console_kit/version'
|
|
8
8
|
require_relative 'console_kit/configuration'
|
|
9
9
|
require_relative 'console_kit/setup'
|
|
10
|
+
require_relative 'console_kit/console_helpers'
|
|
11
|
+
require_relative 'console_kit/prompt'
|
|
10
12
|
require_relative 'console_kit/railtie' if defined?(Rails::Railtie)
|
|
11
13
|
|
|
12
14
|
# Main module for ConsoleKit
|
|
@@ -21,7 +23,7 @@ module ConsoleKit
|
|
|
21
23
|
def reset_configuration!
|
|
22
24
|
@configuration = nil
|
|
23
25
|
Setup.current_tenant = nil
|
|
24
|
-
TenantConfigurator.configuration_success = false
|
|
26
|
+
TenantConfigurator.configuration_success = false if defined?(TenantConfigurator)
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def pretty_output = configuration.pretty_output
|
|
@@ -11,14 +11,20 @@ Rails.application.config.after_initialize do
|
|
|
11
11
|
# constants: {
|
|
12
12
|
# shard: :shard_1,
|
|
13
13
|
# mongo_db: 'mongo_db_1',
|
|
14
|
-
# partner_code: 'partner_a'
|
|
14
|
+
# partner_code: 'partner_a',
|
|
15
|
+
# redis_db: 1,
|
|
16
|
+
# elasticsearch_prefix: 'tenant_a',
|
|
17
|
+
# environment: 'production'
|
|
15
18
|
# }
|
|
16
19
|
# },
|
|
17
20
|
# tenant_b: {
|
|
18
21
|
# constants: {
|
|
19
22
|
# shard: :shard_2,
|
|
20
23
|
# mongo_db: 'mongo_db_2',
|
|
21
|
-
# partner_code: 'partner_b'
|
|
24
|
+
# partner_code: 'partner_b',
|
|
25
|
+
# redis_db: 2,
|
|
26
|
+
# elasticsearch_prefix: 'tenant_b',
|
|
27
|
+
# environment: 'staging'
|
|
22
28
|
# }
|
|
23
29
|
# }
|
|
24
30
|
# }
|
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: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Soumyadeep Pal
|
|
@@ -9,20 +9,6 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
-
- !ruby/object:Gem::Dependency
|
|
13
|
-
name: mongoid
|
|
14
|
-
requirement: !ruby/object:Gem::Requirement
|
|
15
|
-
requirements:
|
|
16
|
-
- - ">="
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0'
|
|
19
|
-
type: :runtime
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - ">="
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0'
|
|
26
12
|
- !ruby/object:Gem::Dependency
|
|
27
13
|
name: rails
|
|
28
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,9 +39,13 @@ files:
|
|
|
53
39
|
- lib/console_kit/configuration.rb
|
|
54
40
|
- lib/console_kit/connections/base_connection_handler.rb
|
|
55
41
|
- lib/console_kit/connections/connection_manager.rb
|
|
42
|
+
- lib/console_kit/connections/elasticsearch_connection_handler.rb
|
|
56
43
|
- lib/console_kit/connections/mongo_connection_handler.rb
|
|
44
|
+
- lib/console_kit/connections/redis_connection_handler.rb
|
|
57
45
|
- lib/console_kit/connections/sql_connection_handler.rb
|
|
46
|
+
- lib/console_kit/console_helpers.rb
|
|
58
47
|
- lib/console_kit/output.rb
|
|
48
|
+
- lib/console_kit/prompt.rb
|
|
59
49
|
- lib/console_kit/railtie.rb
|
|
60
50
|
- lib/console_kit/setup.rb
|
|
61
51
|
- lib/console_kit/tenant_configurator.rb
|