Module: Colours::RainbowColours

Defined in:
lib/colours/rainbow_colours/constants.rb,
lib/colours/rainbow_colours/rainbow.rb,
lib/colours/rainbow_colours/set_mode.rb,
lib/colours/rainbow_colours/println_ani.rb,
lib/colours/rainbow_colours/println_plain.rb,
lib/colours/rainbow_colours/report_errors.rb,
lib/colours/rainbow_colours/returnln_plain.rb,
lib/colours/rainbow_colours/print_rainbow_line.rb,
lib/colours/rainbow_colours/paint_detected_mode.rb,
lib/colours/rainbow_colours/do_parse_via_rainbow_colours.rb,
lib/colours/rainbow_colours/check_for_trollop_being_available_or_exit.rb

Overview

Colours::RainbowColours

Constant Summary collapse

DEFAULT_RAINBOW_SPREAD =
#

Colours::RainbowColours::DEFAULT_RAINBOW_SPREAD

#
3.0
STRIP_ANSI =
#

Colours::RainbowColours::STRIP_ANSI

Checking for escape sequences (and a number).

Official documentation can be seen here:

https://ruby-doc.org/core/Regexp.html#method-c-new
#
Regexp.new('\e\[[\d;]*[m|K]', nil)

Class Method Summary collapse

Class Method Details

.check_for_trollop_being_available_or_exitObject

#

Colours::RainbowColours.check_for_trollop_being_available_or_exit

#


16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/colours/rainbow_colours/check_for_trollop_being_available_or_exit.rb', line 16

def self.check_for_trollop_being_available_or_exit
  # ======================================================================= #
  # First check whether the user has the Trollop gem installed.
  # ======================================================================= #
  unless Object.const_defined? :Trollop
    e
    e 'Trollop is not installed. Please install it via:'
    e
    e '  gem install trollop'
    e
    exit
  end
end

.do_parse_via_rainbow_colours(i = ARGV) ⇒ Object

#

Colours::RainbowColours.do_parse_via_rainbow_colours

This is the rainbow_colours initializer.

#


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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/colours/rainbow_colours/do_parse_via_rainbow_colours.rb', line 36

def self.do_parse_via_rainbow_colours(
    i = ARGV
  )
  check_for_trollop_being_available_or_exit
  # ======================================================================= #
  # Setup the trollop-parser instance next.
  # ======================================================================= #
  trollop_parser = Trollop::Parser.new {
    banner <<HEADER

Usage: rainbow_colours [OPTION]... [FILE]...

Display one or several files. Alternatively, obtain input via STDIN.

With no FILE, or when FILE is -, read standard input.

HEADER
    banner ''
    # ll | rainbowc -a
    opt :animate,   'Enable psychedelics',        short: 'a', :default => false
    opt :spread,    'Rainbow spread',             short: 'p', :default => DEFAULT_RAINBOW_SPREAD
    opt :freq,      'Rainbow frequency',          short: 'F', :default => 0.1
    opt :seed,      'Rainbow seed, 0 = random',   short: 'S', :default => 0
    opt :duration,  'Animation duration',         short: 'd', :default => 12
    opt :speed,     'Animation speed',            short: 's', :default => 20.0
    opt :invert,    'Invert fg and bg',           short: 'i', :default => false
    opt :truecolor, '24-bit (truecolor)',         short: 't', :default => false
    opt :force,     'Force color even when stdout is not a tty', short: 'f', :default => false
    opt :help,      'Show this help menu',        short: 'h'
    banner <<FOOTER

Examples:

rainbow_colours f - g      Output f's contents, then stdin, then g's contents.
rainbow_colours            Copy standard input to standard output.
fortune | rainbow_colours  Display a rainbow cookie.

FOOTER
  } # End of the help-banner.
  # ======================================================================= #
  # Keep the Trollop instance assigned to the variable opts.
  # ======================================================================= #
  opts = Trollop.with_standard_exception_handling(trollop_parser) {
    begin
      o = trollop_parser.parse(i) # Parse the given input here.
    rescue Trollop::HelpNeeded # If we need some more help.
      string_io_buffer = StringIO.new
      trollop_parser.educate(string_io_buffer) # <- display the help message and then exit.
      string_io_buffer.rewind
      opts = {
        animate:  false,
        speed:     20,
        duration:  12,
        os:       rand(256),
        spread:   8.0,
        freq:     0.3
      }
      splitted = string_io_buffer.read.split("\n")
      ::Colours.cat(splitted, opts)
      e
      string_io_buffer.close # Close the buffer again.
      exit 1
    end
    o
  }

  p.die(:spread,   'must be >= 0.1') if opts[:spread]   < 0.1
  p.die(:duration, 'must be >= 0.1') if opts[:duration] < 0.1
  p.die(:speed,    'must be >= 0.1') if opts[:speed]    < 0.1

  # ======================================================================= #
  # Build up the option-hash next.
  # ======================================================================= #
  opts[:os] = opts[:seed]
  opts[:os] = rand(256) if opts[:os] == 0

  begin
    if ARGV.empty?
      files = [:stdin]
    else
      files = ARGV[0..-1]
    end
    files.each { |file|
      case file
      when '-', :stdin
        fd = ARGF
      end
      begin
        fd = File.open(file) unless fd == ARGF
        if $stdout.tty? or opts[:force]
          # =============================================================== #
          # Work on TTY terminals. This also includes terminals such
          # as KDE Konsole. The second argument are the options passed
          # towards Colours.cat().
          # =============================================================== #
          ::Colours.cat(fd, opts)
        else
          until fd.eof? do
            # ============================================================= #
            # Keep on writing the input to $stdout here.
            # ============================================================= #
            $stdout.write(fd.read(8192))
          end
        end
      rescue Errno::ENOENT
        report_no_such_file_or_directory(file)
        exit 1
      rescue Errno::EACCES
        report_permission_denied(file)
        exit 1
      rescue Errno::EISDIR
        report_is_a_directory(file)
        exit 1
      rescue Errno::EPIPE
        exit 1
      end
    }
  rescue Interrupt
  end
end

.e(i = '') ⇒ Object

#

Colours::RainbowColours.e

#


18
19
20
# File 'lib/colours/rainbow_colours/print_rainbow_line.rb', line 18

def self.e(i = '')
  ::Colours.e(i)
end
#

Colours::RainbowColours.print_rainbow_line

This line will do the colourizing part, on a per-line basis.

The second and third arguments are Hashes.

Simple invocation example:

Colours::RainbowColours.print_rainbow_line("Hello world! \n" * 80)
#


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
# File 'lib/colours/rainbow_colours/print_rainbow_line.rb', line 34

def self.print_rainbow_line(
    string       = 'Hello world!',
    defaults     = {},
    options_hash = {}
  )
  if string.is_a? Array
    string = string.join(' ').strip
  end
  options_hash.merge!(defaults)
  string = string.dup if string.frozen?
  string.chomp! if string.include? N
  # ======================================================================= #
  # Next, get rid of ANSI colours, unless the string is nil.
  # ======================================================================= #
  if !string.nil? and ($stdout.tty? or options_hash[:force])
    string.gsub!(STRIP_ANSI, '')
  end
  # ======================================================================= #
  # Replace all tabs with 2 spaces.
  # ======================================================================= #
  string.gsub!("\t", ' ' * 2) if string.include? "\t"
  if options_hash[:animate]
    # ===================================================================== #
    # Handle animated frames, as specified by the options-hash.
    # ===================================================================== #
    println_ani(string, options_hash)
  else
    RainbowColours.println_plain(string, options_hash)
  end
  e
end

.println_ani(str, hash_options = {}) ⇒ Object

#

Colours::RainbowColours.println_ani

This method here will be called if the “animate option” was enabled, that is, set to true.

#


17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/colours/rainbow_colours/println_ani.rb', line 17

def self.println_ani(
    str, hash_options = {}
  )
  return if str.empty? # Return early in this case.
  (1 .. hash_options[:duration]).each { |irrelevant_variable|
    print "\e[#{str.length}D"
    hash_options[:os] += hash_options[:spread]
    # ===================================================================== #
    # Next, delegate to println_plain():
    # ===================================================================== #
    println_plain(str, hash_options)
    sleep(1.0/hash_options[:speed])
  }
end

.println_plain(str = "Hello world! Good morning sunshine.\n", defaults = {}, opts = { spread: DEFAULT_RAINBOW_SPREAD, os: rand(256), truecolor: true }) ⇒ Object

#

Colours::RainbowColours.println_plain

This method will colourize the given input line.

Usage example:

Colours::RainbowColours.println_plain
Colours::RainbowColours.println_plain 'one two three four five six seven eight'
#


24
25
26
27
28
29
30
31
32
33
34
# File 'lib/colours/rainbow_colours/println_plain.rb', line 24

def self.println_plain(
    str      = "Hello world! Good morning sunshine.\n",
    defaults = {},
    opts     = {
      spread:    DEFAULT_RAINBOW_SPREAD,
      os:        rand(256),
      truecolor: true
    }
  )
  print returnln_plain(str, defaults, opts)
end

.rainbow(frequency = 0.1, i) ⇒ Object

#

Colours::RainbowColours.rainbow

Do the colour calculations next, via the rainbow() method.

The frequency is typically a value such as 0.1.

We will calculate the RGB values here (R, G, B).

Invocation example:

Colours::RainbowColours.rainbow(:default, 3)  # => "#A5D604"
Colours::RainbowColours.rainbow(:default, 12) # => "#F66C1C"
#


24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/colours/rainbow_colours/rainbow.rb', line 24

def self.rainbow(
    frequency = 0.1, i
  )
  case frequency
  when nil,
       :default
    frequency = 0.1
  end
  red   = Math.sin(frequency * i + 0) * 127 + 128
  green = Math.sin(frequency * i + 2 * Math::PI/3) * 127 + 128
  blue  = Math.sin(frequency * i + 4 * Math::PI/3) * 127 + 128
  '#%02X%02X%02X' % [ red, green, blue ]
end

.report_is_a_directory(i) ⇒ Object

#

Colours::RainbowColours.report_is_a_directory

#


21
22
23
# File 'lib/colours/rainbow_colours/report_errors.rb', line 21

def self.report_is_a_directory(i)
  e "Colours::RainbowColours: #{i}: Is a directory"
end

.report_no_such_file_or_directory(i) ⇒ Object

#

Colours::RainbowColours.report_no_such_file_or_directory

#


14
15
16
# File 'lib/colours/rainbow_colours/report_errors.rb', line 14

def self.report_no_such_file_or_directory(i)
  e "Colours::RainbowColours: #{i}: No such file or directory"
end

.report_permission_denied(i) ⇒ Object

#

Colours::RainbowColours.report_permission_denied

#


28
29
30
# File 'lib/colours/rainbow_colours/report_errors.rb', line 28

def self.report_permission_denied(i)
  e "Colours::RainbowColours: #{i}: Permission denied"
end

.returnln_plain(str = "Hello world! Good morning sunshine.\n", defaults = {}, options_hash = { spread: DEFAULT_RAINBOW_SPREAD, os: rand(256), truecolor: true }) ⇒ Object

#

Colours::RainbowColours.returnln_plain

This will return a rainbow-coloured text.

The first argument is the text that you wish to display.

Invocation example:

puts Colours::RainbowColours.returnln_plain('abc def ' * 1000)
#


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
# File 'lib/colours/rainbow_colours/returnln_plain.rb', line 26

def self.returnln_plain(
    str          = "Hello world! Good morning sunshine.\n",
    defaults     = {},
    options_hash = {
      spread:    DEFAULT_RAINBOW_SPREAD,
      os:        rand(256),
      truecolor: true
    }
  )
  options_hash.merge!(defaults)
  RainbowColours.set_mode(options_hash[:truecolor])
  the_chars = str.chomp.chars # We obtain the individual characters here.
  # ======================================================================= #
  # Iterate over the chars. i, aka the second argument, is the index.
  # ======================================================================= #
  result = ''.dup
  the_chars.each_with_index { |character, index|
    # ===================================================================== #
    # Delegate towards the toplevel-method rainbow().
    # ===================================================================== #
    division_result = (
      options_hash[:os]+index / options_hash[:spread]
    )
    # ===================================================================== #
    # Next, we will obtain some ASCII value such as '#A5D604':
    # ===================================================================== #
    code = RainbowColours.rainbow(options_hash[:freq], division_result)
    # ===================================================================== #
    # Delegate towards Paint[] next. The painted character will be
    # displayed one-per-char.
    # This will yield a String such as "\e[38;2;16;236;130mo\e[0m".
    # ===================================================================== #
    result << Paint[character, *[ (:black if options_hash[:invert]), code ].compact ]
  }
  return result
end

.set_mode(truecolor) ⇒ Object

#

Colours::RainbowColours.set_mode

#


14
15
16
17
18
19
20
21
22
# File 'lib/colours/rainbow_colours/set_mode.rb', line 14

def self.set_mode(truecolor)
  begin
    unless Object.const_defined? :Paint
      require 'paint'
    end
    @paint_mode_detected ||= Paint.mode
    Paint.mode = truecolor ? 0xffffff : @paint_mode_detected
  rescue LoadError; end
end