console_kit 0.1.2 → 0.1.3

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: 8c68e98f9ea76d56039a31aee01f4642c23bc89e9b41868f988efff84f891647
4
- data.tar.gz: 87be1db75fcb8d80851409a1903fe136d09e6275e5db9797b74f18df28603c63
3
+ metadata.gz: 2171674e2c587a2bb53387614758fefb8c6542f7ddb88c1b9dc55f85bf5a481a
4
+ data.tar.gz: 0fe05d8f5e3ee8d8814bba1b1eef1c3233b5c1996d805a4e2b06615326771480
5
5
  SHA512:
6
- metadata.gz: f339cda7362c7a913918b992395ebd84ab4db2601a8c4d778302ae2579a1adfffab19d8eb63ca669b1d669ed9c58b589bfbe18c0d4ce758b83667b5935ebf304
7
- data.tar.gz: c171cf918f2ea0ebe30c292f7b09847f3974c2fc39c55e21e36845c0399e04d7bc1b6558a8022bf860a57ae25403148ce62ef58748a7aad527ef55a870e6c5dd
6
+ metadata.gz: 763f319a96484ab677cad3eab21cc929e2775a98cf3d085db4fd317db8523f8220a3bcd808cedb3df857d193d1da9544de47a1263a8192a50888352e2fe1a575
7
+ data.tar.gz: 7acf3b5134fd7155ca4ceaadd44d07e93bf2f48fe322d4133055e11a8436157dcdf7b3ed7b303c76149674beccbd26780d5883590659aa040a8f55f8a7792cf0
data/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [0.1.3] - 2025-08-12
10
+ ### Added
11
+ - `ConsoleKit.current_tenant` method to retrieve the current tenant at runtime.
12
+ - `ConsoleKit.reset_current_tenant` to reset tenant selection.
13
+ - `pretty_output` configuration added with ability to manually toggle CLI verbosity.
14
+
15
+ ### Changed
16
+ - Refactored internal logic for improved maintainability and future extensibility.
17
+ - Enhanced test coverage for better reliability and edge case handling.
18
+
19
+ ---
20
+
9
21
  ## [0.1.2] - 2025-07-23
10
22
  ### Added
11
23
  - Changelog added.
@@ -38,6 +50,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
38
50
 
39
51
  ## [Unreleased]
40
52
 
53
+ [0.1.3]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.3
41
54
  [0.1.2]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.2
42
55
  [0.1.1]: https://github.com/Soumyadeep-ai/console_kit/releases/tag/v0.1.1
43
56
  [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
+ ![Gem Version](https://img.shields.io/gem/v/console_kit.svg)
4
+ ![Gem Downloads](https://img.shields.io/gem/dt/console_kit.svg)
5
+ ![Build Status](https://github.com/Soumyadeep-ai/console_kit/actions/workflows/release.yml/badge.svg)
6
+ ![License](https://img.shields.io/github/license/Soumyadeep-ai/console_kit)
7
+ ![Ruby](https://img.shields.io/badge/ruby-%3E=3.1.0-red)
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
 
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConsoleKit
4
+ # Stores configurations
5
+ class Configuration
6
+ attr_accessor :pretty_output, :tenants, :context_class
7
+
8
+ def initialize(pretty_output: true, tenants: nil, context_class: nil)
9
+ @pretty_output = pretty_output
10
+ @tenants = tenants
11
+ @context_class = context_class
12
+ end
13
+ end
14
+ end
@@ -3,40 +3,47 @@
3
3
  module ConsoleKit
4
4
  # Handles Console outputs
5
5
  module Output
6
- class << self
7
- def print_error(text)
8
- print_message("[āœ—] #{text}", '1;31') # Red
9
- end
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
- def print_success(text)
12
- print_message("[āœ“] #{text}", '1;32') # Green
17
+ class << self
18
+ TYPES.each_key do |type|
19
+ define_method("print_#{type}") do |text|
20
+ formatted = (type == :header ? "\n=== #{text} ===" : text)
21
+ print_with(type, formatted)
22
+ end
13
23
  end
14
24
 
15
25
  def print_backtrace(exception)
16
- exception.backtrace.each { |line| print_message(" #{line}", '0;90') } # Dim gray
17
- end
18
-
19
- def print_header(text)
20
- print_message("\n=== #{text} ===", '1;34') # Bold Blue
26
+ exception&.backtrace&.each { |line| print_with(:trace, " #{line}") }
21
27
  end
22
28
 
23
- def print_info(text)
24
- print_message(text)
25
- end
29
+ private
26
30
 
27
- def print_prompt(text)
28
- print_message(text, '1;36') # Cyan
31
+ def print_with(type, text, timestamp: false)
32
+ meta = TYPES[type]
33
+ message = build_message(text, meta[:symbol], timestamp)
34
+ output(message, meta[:color])
29
35
  end
30
36
 
31
- def print_warning(text)
32
- print_message("[!] #{text}", '1;33') # Yellow
37
+ def build_message(text, symbol, timestamp)
38
+ time = timestamp ? "[#{Time.current.strftime('%Y-%m-%d %H:%M:%S')}] " : ''
39
+ sym = symbol ? "#{symbol} " : ''
40
+ "#{PREFIX} #{time}#{sym}#{text}"
33
41
  end
34
42
 
35
- private
43
+ def output(message, color)
44
+ return puts message unless ConsoleKit.configuration.pretty_output && color
36
45
 
37
- def print_message(text, color = nil)
38
- msg = "[ConsoleKit] #{text}"
39
- puts color ? "\e[#{color}m#{msg}\e[0m" : msg
46
+ puts "\e[#{color}m#{message}\e[0m"
40
47
  end
41
48
  end
42
49
  end
@@ -3,8 +3,6 @@
3
3
  module ConsoleKit
4
4
  # Railtie
5
5
  class Railtie < Rails::Railtie
6
- console do
7
- ConsoleKit.setup
8
- end
6
+ console { ConsoleKit::Setup.setup }
9
7
  end
10
8
  end
@@ -6,46 +6,72 @@ require_relative 'output'
6
6
 
7
7
  # Core Logic for initial Setup
8
8
  module ConsoleKit
9
- class << self
10
- attr_accessor :tenants, :context_class
9
+ # Does the initial setup
10
+ module Setup
11
+ class << self
12
+ attr_reader :current_tenant
11
13
 
12
- def setup
13
- return Output.print_error('No tenants configured.') if tenants.nil? || tenants.empty?
14
+ def setup
15
+ return Output.print_error('No tenants configured.') if no_tenants?
14
16
 
15
- tenant_key = resolve_tenant_key
16
- return Output.print_error('No tenant selected. Loading without tenant configuration.') unless tenant_key
17
+ key = select_tenant_key
18
+ return Output.print_error('No tenant selected. Loading without tenant configuration.') unless key
17
19
 
18
- initialize_tenant(tenant_key)
19
- rescue StandardError => e
20
- handle_setup_error(e)
21
- end
20
+ configure(key)
21
+ rescue StandardError => e
22
+ handle_error(e)
23
+ end
22
24
 
23
- def configure
24
- yield self
25
- end
25
+ def tenant_setup_successful? = !@current_tenant.to_s.empty?
26
26
 
27
- private
27
+ def reset_current_tenant
28
+ return warn_no_tenants unless tenants?
28
29
 
29
- def resolve_tenant_key
30
- single_tenant? || non_interactive? ? tenants.keys.first : TenantSelector.select(tenants, tenants.keys)
31
- end
30
+ warn_reset if @current_tenant
31
+ TenantConfigurator.clear(ConsoleKit.configuration.context_class) if @current_tenant
32
32
 
33
- def single_tenant?
34
- tenants.size == 1
35
- end
33
+ @current_tenant = nil
34
+ setup
35
+ tenant_setup_successful?
36
+ end
36
37
 
37
- def non_interactive?
38
- !$stdin.tty?
39
- end
38
+ private
40
39
 
41
- def initialize_tenant(tenant_key)
42
- TenantConfigurator.configure_tenant(tenant_key, tenants, context_class)
43
- Output.print_success("Tenant initialized: #{tenant_key}")
44
- end
40
+ def configure(key)
41
+ return unless TenantConfigurator.configure_tenant(key, tenants, context_class)
42
+
43
+ @current_tenant = key
44
+ Output.print_success("Tenant initialized: #{key}")
45
+ end
46
+
47
+ def tenants = ConsoleKit.configuration.tenants
48
+ def context_class = ConsoleKit.configuration.context_class
49
+ def tenants? = tenants&.any?
50
+ def no_tenants? = !tenants?
51
+
52
+ def select_tenant_key
53
+ return tenants.keys.first if auto_select?
54
+
55
+ TenantSelector.select(tenants, tenants.keys)
56
+ end
57
+
58
+ def auto_select? = single_tenant? || non_interactive?
59
+ def single_tenant? = tenants.size == 1
60
+ def non_interactive? = !$stdin.tty?
61
+
62
+ def warn_no_tenants
63
+ Output.print_warning('Cannot reset tenant: No tenants configured.')
64
+ false
65
+ end
66
+
67
+ def warn_reset
68
+ Output.print_warning("Resetting tenant: #{@current_tenant}")
69
+ end
45
70
 
46
- def handle_setup_error(error)
47
- Output.print_error("Error setting up tenant: #{error.message}")
48
- Output.print_backtrace(error)
71
+ def handle_error(error)
72
+ Output.print_error("Error setting up tenant: #{error.message}")
73
+ Output.print_backtrace(error)
74
+ end
49
75
  end
50
76
  end
51
77
  end
@@ -7,31 +7,66 @@ module ConsoleKit
7
7
  module TenantConfigurator
8
8
  class << self
9
9
  def configure_tenant(key, tenants, context_class)
10
- config = tenants[key]
11
- return Output.print_error("No configuration found for tenant: #{key}") unless config
10
+ constants = tenants[key]&.[](:constants)
11
+ return missing_config_error(key) unless constants
12
12
 
13
- constants = config[:constants]
13
+ validate_constants!(constants)
14
14
  apply_context(context_class, constants)
15
- setup_database_connections(context_class)
15
+ setup_connections(context_class)
16
16
 
17
17
  Output.print_success("Tenant set to: #{key}")
18
+ true
18
19
  rescue StandardError => e
19
- Output.print_error("Failed to configure tenant '#{key}': #{e.message}")
20
- Output.print_backtrace(e)
20
+ handle_error(e, key)
21
+ false
22
+ end
23
+
24
+ def clear(context_class)
25
+ %i[tenant_shard tenant_mongo_db partner_identifier].each do |attr|
26
+ context_class.public_send("#{attr}=", nil)
27
+ end
28
+ Output.print_info('Tenant context has been cleared.')
21
29
  end
22
30
 
23
31
  private
24
32
 
25
- def apply_context(context_class, constants)
26
- context_class.tenant_shard = constants[:shard]
27
- context_class.tenant_mongo_db = constants[:mongo_db]
28
- context_class.partner_identifier = constants[:partner_code]
33
+ def validate_constants!(constants)
34
+ missing = %i[shard partner_code] - constants.keys
35
+ raise "Tenant constants missing keys: #{missing.join(', ')}" unless missing.empty?
36
+ end
37
+
38
+ def missing_config_error(key)
39
+ Output.print_error("No configuration found for tenant: #{key}")
40
+ false
41
+ end
42
+
43
+ def apply_context(ctx, constant)
44
+ ctx.tenant_shard = constant[:shard]
45
+ ctx.tenant_mongo_db = constant[:mongo_db]
46
+ ctx.partner_identifier = constant[:partner_code]
47
+ end
48
+
49
+ def setup_connections(ctx)
50
+ ApplicationRecord.establish_connection(ctx.tenant_shard.to_sym) if defined?(ApplicationRecord)
51
+ return unless defined?(Mongoid) && Mongoid.respond_to?(:override_client)
52
+ return if ctx.tenant_mongo_db.nil? || ctx.tenant_mongo_db.empty?
53
+
54
+ client = ctx.tenant_mongo_db.to_s
55
+ Mongoid.override_client(client)
29
56
  end
30
57
 
31
58
  def setup_database_connections(context_class)
32
- ApplicationRecord.establish_connection(context_class.tenant_shard.to_sym)
59
+ ApplicationRecord.establish_connection(context_class.tenant_shard.to_sym) if defined?(ApplicationRecord)
60
+ return unless defined?(Mongoid) && Mongoid.respond_to?(:override_client)
61
+ return if context_class.tenant_mongo_db.nil? || context_class.tenant_mongo_db.empty?
62
+
33
63
  Mongoid.override_client(context_class.tenant_mongo_db.to_s)
34
64
  end
65
+
66
+ def handle_error(error, key)
67
+ Output.print_error("Failed to configure tenant '#{key}': #{error.message}")
68
+ Output.print_backtrace(error)
69
+ end
35
70
  end
36
71
  end
37
72
  end
@@ -9,11 +9,12 @@ module ConsoleKit
9
9
  def select(tenants, keys)
10
10
  print_tenant_selection_menu(tenants, keys)
11
11
 
12
- max_attempts = 3
13
- max_attempts.times do
12
+ 3.times do |attempt|
14
13
  index = prompt_user_for_selection(keys.size)
15
14
  return nil if index.zero?
16
15
  return keys[index - 1] if index.positive?
16
+
17
+ print_tenant_selection_menu(tenants, keys) if attempt < 2
17
18
  end
18
19
 
19
20
  nil
@@ -35,7 +36,6 @@ module ConsoleKit
35
36
  Output.print_prompt("\nEnter the number of the tenant you want (or press Enter for default '1'): ")
36
37
  input = $stdin.gets&.chomp&.strip
37
38
  input = '1' if input.to_s.empty?
38
-
39
39
  return invalid_input_response unless valid_integer?(input)
40
40
 
41
41
  parsed_index = input.to_i
@@ -44,14 +44,8 @@ module ConsoleKit
44
44
  parsed_index
45
45
  end
46
46
 
47
- def valid_integer?(input)
48
- input.match?(/\A\d+\z/)
49
- end
50
-
51
- def invalid_input_response
52
- Output.print_warning('Invalid input. Please enter a number.')
53
- -1
54
- end
47
+ def valid_integer?(input) = input.match?(/\A\d+\z/)
48
+ def invalid_input_response = Output.print_warning('Invalid input. Please enter a number.').then { - 1 }
55
49
 
56
50
  def invalid_range_response(max_index)
57
51
  Output.print_warning("Selection must be between 0 and #{max_index}.")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConsoleKit
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
data/lib/console_kit.rb CHANGED
@@ -1,10 +1,29 @@
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
8
10
  class Error < StandardError; end
9
- # Your code goes here...
11
+
12
+ class << self
13
+ def configure = yield(configuration)
14
+
15
+ def configuration = Thread.current[:console_kit_configuration] ||= Configuration.new
16
+ def reset_configuration! = Thread.current[:console_kit_configuration] = nil
17
+
18
+ %i[pretty_output tenants context_class].each do |name|
19
+ define_method(name) { configuration.public_send(name) }
20
+ define_method("#{name}=") { |val| configuration.public_send("#{name}=", val) }
21
+ end
22
+
23
+ def current_tenant = Setup.current_tenant
24
+ def reset_current_tenant = Setup.reset_current_tenant
25
+
26
+ def enable_pretty_output = configuration.pretty_output = true
27
+ def disable_pretty_output = configuration.pretty_output = false
28
+ end
10
29
  end
@@ -9,20 +9,25 @@ 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
13
15
  initializer_path = Rails.root.join('config', 'initializers', 'console_kit.rb')
14
16
 
15
- if File.exist?(initializer_path)
16
- say_status('skipped', "Initializer already exists: #{initializer_path}", :yellow)
17
+ if File.exist?(initializer_path) && !options[:force]
18
+ say_status :skipped, "Initializer already exists: #{initializer_path}", :yellow
17
19
  else
18
- template 'console_kit.rb', 'config/initializers/console_kit.rb'
19
- say_status('created', "Initializer generated at #{initializer_path}", :green)
20
+ template 'console_kit.rb', 'config/initializers/console_kit.rb', force: options[:force]
21
+ say_status :created, "Initializer generated at #{initializer_path}", :green
20
22
  end
21
23
  end
22
24
 
23
25
  def remind_about_customization
24
26
  say "\nāœ… Setup complete!", :green
25
- say 'šŸ‘‰ Please update `config/initializers/console_kit.rb` to set your `tenants` and `context_class`.', :green
27
+ say 'šŸ“„ Modify `config/initializers/console_kit.rb`:', :green
28
+ %w[tenants context_class].each do |field|
29
+ say " - Set `#{field}` (required)", :green
30
+ end
26
31
  end
27
32
  end
28
33
  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 in the following format
8
+ # TODO: Set your tenants source, example:
9
9
  # {
10
- # key:
11
- # {
12
- # constants:
13
- # {
14
- # shard: # Active Record Shard
15
- # mongo_db: # Mongo Shard (If Mongo Is being used)
16
- # partner_code: # If partners are needed
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.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soumyadeep Pal
@@ -52,6 +52,7 @@ files:
52
52
  - README.md
53
53
  - Rakefile
54
54
  - lib/console_kit.rb
55
+ - lib/console_kit/configuration.rb
55
56
  - lib/console_kit/output.rb
56
57
  - lib/console_kit/railtie.rb
57
58
  - lib/console_kit/setup.rb