Module: Kolor::Extra

Included in:
ColorizedString, String
Defined in:
lib/kolor/extra.rb

Overview

Extended functionality for Kolor including RGB colors, gradients, and themes

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.define_all_themesvoid

This method returns an undefined value.

Defines all built-in themes from Kolor::Enum::Theme



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/kolor/extra.rb', line 267

def self.define_all_themes
  return if @themes_initialized
  Kolor::Logger.info('Built-in themes initialized') unless @themes_initialized
  @themes_initialized = true

  Kolor::Enum::Theme.keys.each do |name|
    config = Kolor::Enum::Theme[name].value
    next unless config.is_a?(Hash)

    styles = []
    styles << config[:foreground] if config[:foreground]
    styles << "on_#{config[:background]}".to_sym if config[:background]
    styles += config[:styles] if config[:styles].is_a?(Array)

    define_theme_method(name, styles)
  end
end

.define_theme_method(name, styles) ⇒ void

This method returns an undefined value.

Defines a theme method on String and ColorizedString

Parameters:

  • name (Symbol)

    theme name

  • styles (Array<Symbol>)

    list of style methods to apply



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/kolor/extra.rb', line 178

def self.define_theme_method(name, styles)
  # Define the method on both String and ColorizedString
  [String, Kolor::ColorizedString].each do |klass|
    klass.class_eval do
      # Remove existing method if present to avoid warnings
      remove_method(name) if method_defined?(name)

      # Define the theme method
      define_method(name) do
        # Return plain string if colors are disabled
        return to_s unless Kolor.enabled?

        # Apply each style in sequence
        result = self
        styles.each do |style|
          # Verify the style method exists before calling it
          if result.respond_to?(style)
            result = result.public_send(style)
          else
            # Log warning but continue with other styles
            Kolor::Logger.debug "Style '#{style}' not found for theme '#{name}'"
          end
        end

        result
      end
    end
  end

  Kolor::Logger.debug "Defined theme method '#{name}' with styles #{styles.inspect}"
end

.get_theme(name) ⇒ Object?

Retrieves the configuration object for a given theme.

Parameters:

  • name (Symbol)

    theme name

Returns:

  • (Object, nil)

    theme configuration or nil if not found or invalid Expected keys:

    - :foreground 


244
245
246
# File 'lib/kolor/extra.rb', line 244

def self.get_theme(name)
  Kolor::Enum::Theme[name]&.value.then { |v| v.is_a?(Hash) ? v : nil }
end

.normalize_styles(styles) ⇒ Hash{Symbol=>Symbol, Array<Symbol>}

Normalizes a list of style symbols into a structured theme hash. Extracts foreground, background, and remaining styles.

Parameters:

  • styles (Array<Symbol>)

    list of style names (e.g., :green, :on_red, :bold)

Returns:

  • (Hash{Symbol=>Symbol, Array<Symbol>})

    normalized theme structure

    • :foreground → foreground color (Symbol or nil)

    • :background → background color (Symbol or nil)

    • :styles → remaining style modifiers (Array<Symbol>)



218
219
220
221
222
223
224
225
226
227
# File 'lib/kolor/extra.rb', line 218

def self.normalize_styles(styles)
  fg = styles.find { |s| Kolor::Enum::Foreground.keys.include?(s) }
  bg = styles.find { |s| s.to_s.start_with?('on_') }
  rest = styles - [fg, bg].compact
  {
    foreground: fg,
    background: bg&.to_s&.sub(/^on_/, '')&.to_sym,
    styles: rest
  }
end

.remove_theme(name) ⇒ Object

Raises:

  • (ArgumentError)


248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/kolor/extra.rb', line 248

def self.remove_theme(name)
  theme = Kolor::Enum::Theme[name]
  raise ArgumentError, "Theme #{name} not found" unless theme

  built_in = i[success error warning info debug]
  raise ArgumentError, "Cannot remove built-in theme #{name}" if built_in.include?(name)

  Kolor::Logger.info("Removing theme #{name}")
  Kolor::Enum::Theme.remove(name)

  [String, Kolor::ColorizedString].each do |klass|
    klass.class_eval do
      remove_method(name) if method_defined?(name)
    end
  end
end

.theme(name, *styles) ⇒ Object

Define a custom theme

Examples:

Kolor::Extra.theme(:error, :white, :on_red, :bold)
"Error!".error # => white text on red background, bold

Parameters:

  • name (Symbol)

    theme name

  • styles (Array<Symbol>)

    list of color and style names to apply



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/kolor/extra.rb', line 156

def self.theme(name, *styles)
  begin
    Kolor::Enum::Theme.entry(name, normalize_styles(styles))
    define_theme_method(name, styles)
  rescue ArgumentError => e
    if e.message =~ /already assigned to (\w+)/
      existing = Regexp.last_match(1)
      Kolor::Logger.warn("Theme value already registered as :#{existing}. Skipping :#{name}.")
    else
      Kolor::Logger.error("Theme registration failed for #{name}: #{e.message}")
    end
  rescue TypeError => e
    Kolor::Logger.error("Theme registration failed for #{name}: #{e.message}")
  end
end

.theme_defined?(name) ⇒ Boolean

Check if a theme method is defined

Parameters:

  • name (Symbol)

    theme name

Returns:

  • (Boolean)

    true if the method exists on String



288
289
290
# File 'lib/kolor/extra.rb', line 288

def self.theme_defined?(name)
  String.method_defined?(name.to_sym)
end

.themesArray<Symbol>

Returns the list of all registered theme names.

Returns:

  • (Array<Symbol>)

    list of theme keys



232
233
234
# File 'lib/kolor/extra.rb', line 232

def self.themes
  Kolor::Enum::Theme.keys
end

Instance Method Details

#color(code) ⇒ String, ColorizedString

256-color palette support

Parameters:

  • code (Integer)

    color code (0-255)

Returns:



40
41
42
# File 'lib/kolor/extra.rb', line 40

def color(code)
  create_colorized_string("\e[38;5;#{code}m")
end

#gradient(start_color, end_color) ⇒ String

Gradient between two colors

Parameters:

  • start_color (Symbol)

    starting color name

  • end_color (Symbol)

    ending color name

Returns:

  • (String)

    string with gradient effect



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/kolor/extra.rb', line 107

def gradient(start_color, end_color)
  return to_s unless Kolor.enabled?

  start_enum = Kolor::Enum::Foreground[start_color]
  end_enum = Kolor::Enum::Foreground[end_color]

  return to_s unless start_enum && end_enum

  start_code = start_enum.value
  end_code = end_enum.value

  chars = to_s.chars
  return Kolor.clear_code if chars.empty?

  result = chars.map.with_index do |char, i|
    progress = chars.length > 1 ? i.to_f / (chars.length - 1) : 0
    color_code = start_code + ((end_code - start_code) * progress).round
    "\e[#{color_code}m#{char}"
  end

  result.join + Kolor.clear_code
end

#on_color(code) ⇒ String, ColorizedString

256-color background palette support

Parameters:

  • code (Integer)

    color code (0-255)

Returns:



47
48
49
# File 'lib/kolor/extra.rb', line 47

def on_color(code)
  create_colorized_string("\e[48;5;#{code}m")
end

#on_hex(hex_color) ⇒ String, ColorizedString

Hex color support (background)

Parameters:

  • hex_color (String)

    with_hex color code (with or without #)

Returns:



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/kolor/extra.rb', line 91

def on_hex(hex_color)
  return to_s unless Kolor.enabled?

  hex_color = hex_color.delete('#')
  return to_s unless hex_color.match?(/^[0-9A-Fa-f]{6}$/)

  r = hex_color[0..1].to_i(16)
  g = hex_color[2..3].to_i(16)
  b = hex_color[4..5].to_i(16)
  on_rgb(r, g, b)
end

#on_rgb(red, green, blue) ⇒ String, ColorizedString

RGB / True color support (background)

Parameters:

  • red (Integer)

    red component (0-255)

  • green (Integer)

    green component (0-255)

  • blue (Integer)

    blue component (0-255)

Returns:



67
68
69
70
71
# File 'lib/kolor/extra.rb', line 67

def on_rgb(red, green, blue)
  return to_s unless Kolor.enabled?
  return to_s unless [red, green, blue].all? { |v| v.between?(0, 255) }
  create_colorized_string("\e[48;2;#{red};#{green};#{blue}m")
end

#rainbowString

Rainbow colors

Returns:

  • (String)

    string with rainbow effect



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/kolor/extra.rb', line 132

def rainbow
  return to_s unless Kolor.enabled?

  colors = i[red yellow green cyan blue magenta]
  chars = to_s.chars

  return Kolor.clear_code if chars.empty?

  result = chars.map.with_index do |char, i|
    color = colors[i % colors.length]
    enum = Kolor::Enum::Foreground[color]
    code = enum.value
    "\e[#{code}m#{char}"
  end

  result.join + Kolor.clear_code
end

#rgb(red, green, blue) ⇒ String, ColorizedString

RGB / True color support (foreground)

Parameters:

  • red (Integer)

    red component (0-255)

  • green (Integer)

    green component (0-255)

  • blue (Integer)

    blue component (0-255)

Returns:



56
57
58
59
60
# File 'lib/kolor/extra.rb', line 56

def rgb(red, green, blue)
  return to_s unless Kolor.enabled?
  return to_s unless [red, green, blue].all? { |v| v.between?(0, 255) }
  create_colorized_string("\e[38;2;#{red};#{green};#{blue}m")
end

#with_hex(hex_color) ⇒ String, ColorizedString

Hex color support (foreground)

Parameters:

  • hex_color (String)

    with_hex color code (with or without #)

Returns:



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/kolor/extra.rb', line 76

def with_hex(hex_color)
  return to_s unless Kolor.enabled?

  hex_color = hex_color.delete('#')
  return to_s unless hex_color.match?(/^[0-9A-Fa-f]{6}$/)

  r = hex_color[0..1].to_i(16)
  g = hex_color[2..3].to_i(16)
  b = hex_color[4..5].to_i(16)
  rgb(r, g, b)
end