summaryrefslogtreecommitdiff
path: root/lib/highline/string_extensions.rb
blob: bdeaf5a228e44000d7dba9f0f080ae6c32a5d722 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# coding: utf-8

class HighLine
  # Returns a HighLine::String from any given String.
  # @param s [String]
  # @return [HighLine::String] from the given string.
  def self.String(s) # rubocop:disable Naming/MethodName
    HighLine::String.new(s)
  end

  # HighLine extensions for String class.
  # Included by HighLine::String.
  module StringExtensions
    STYLE_METHOD_NAME_PATTERN = /^(on_)?rgb_([0-9a-fA-F]{6})$/

    # Included hook. Actions to take when being included.
    # @param base [Class, Module] base class
    def self.included(base)
      define_builtin_style_methods(base)
      define_style_support_methods(base)
    end

    # At include time, it defines all basic style
    # support method like #color, #on, #uncolor,
    # #rgb, #on_rgb and the #method_missing callback
    # @return [void]
    # @param base [Class, Module] the base class/module
    #
    def self.define_style_support_methods(base)
      base.class_eval do
        undef :color if method_defined? :color
        undef :foreground if method_defined? :foreground
        def color(*args)
          self.class.new(HighLine.color(self, *args))
        end
        alias_method :foreground, :color

        undef :on if method_defined? :on
        def on(arg)
          color(("on_" + arg.to_s).to_sym)
        end

        undef :uncolor if method_defined? :uncolor
        def uncolor
          self.class.new(HighLine.uncolor(self))
        end

        undef :rgb if method_defined? :rgb
        def rgb(*colors)
          color_code = setup_color_code(*colors)
          color("rgb_#{color_code}".to_sym)
        end

        undef :on_rgb if method_defined? :on_rgb
        def on_rgb(*colors)
          color_code = setup_color_code(*colors)
          color("on_rgb_#{color_code}".to_sym)
        end

        # @todo Chain existing method_missing?
        undef :method_missing if method_defined? :method_missing
        def method_missing(method, *_args)
          if method.to_s =~ STYLE_METHOD_NAME_PATTERN
            color(method)
          else
            super
          end
        end

        undef :respond_to_missing if method_defined? :respond_to_missing
        def respond_to_missing?(method_name, include_private = false)
          method_name.to_s =~ STYLE_METHOD_NAME_PATTERN || super
        end

        private

        def setup_color_code(*colors)
          color_code = colors.map do |color|
            if color.is_a?(Numeric)
              format("%02x", color)
            else
              color.to_s
            end
          end.join

          raise "Bad RGB color #{colors.inspect}" unless
            color_code =~ /^[a-fA-F0-9]{6}/

          color_code
        end
      end
    end

    # At include time, it defines all basic builtin styles.
    # @param base [Class, Module] base Class/Module
    def self.define_builtin_style_methods(base)
      HighLine::COLORS.each do |color|
        color = color.downcase
        base.class_eval <<-END
          undef :#{color} if method_defined? :#{color}
          def #{color}
            color(:#{color})
          end
        END

        base.class_eval <<-END
          undef :on_#{color} if method_defined? :on_#{color}
          def on_#{color}
            on(:#{color})
          end
        END

        HighLine::STYLES.each do |style|
          style = style.downcase
          base.class_eval <<-END
            undef :#{style} if method_defined? :#{style}
            def #{style}
              color(:#{style})
            end
          END
        end
      end
    end
  end

  # Adds color support to the base String class
  def self.colorize_strings
    ::String.send(:include, StringExtensions)
  end
end