Вызов метода на строку при создании нового строкового объекта - PullRequest
0 голосов
/ 17 мая 2019

Хорошо, поэтому у меня есть метод colourize, динамически пропатченный в строковом классе:

class String
    @@colours = [[154, 184, 208, 203, 198, 164, 129, 92], [63, 33, 39, 44, 49, 83, 118], [40,41,42,43,211, 210, 209, 208]].tap { |itself| itself.concat(itself.map(&:reverse)) }

    define_method(:colourize) do |final = ''|
        colour = @@colours.sample
        colour_size = colour.size - 1
        index, div, val = 0, length / colour_size, ''
        div = 1 if div == 0
        colour_size -= 1

        each_char.with_index do |c, i|
            index += 1 if (i % div == 0 && index < colour_size) && i > 1
            val.concat("\e[38;5;#{colour[index]}m#{c}")
        end

        val + "\e[0m" + final
    end
end

Это окрашивает объект String (тестируется только в оболочке BASH в системе Linux).Но мне приходится каждый раз писать метод colourize.

puts 'Hello World'.colourize

Есть ли способ исправить строку так, чтобы при создании или назначении переменной Hello World онпо умолчанию вызовет метод colourize?

Patching String # intialize не просто работает.

1 Ответ

3 голосов
/ 17 мая 2019

Есть ли способ исправить строку, чтобы […] он по умолчанию вызывал метод colourize?

Это плохая идея. Строки являются фундаментальными для Ruby, как массивы, хэши и символы. Помещение цветовых кодов в каждую созданную строку, несмотря ни на что, скорее всего что-то сломает.

Вы должны добавлять цветовые коды только при фактической печати строки (и, вероятно, только при печати на tty).

Это подводит нас к puts. Он записывает данные объекты в стандартный формат, преобразовывая их в строки при необходимости. К сожалению, строка не нуждается ни в каком преобразовании, поэтому вызов to_s пропускается, и у нас нет метода для подключения.

Но вместо исправления String мы могли бы исправить верхний уровень puts:

(я использую upcase в демонстрационных целях, потому что здесь я не могу отобразить escape-последовательности ANSI)

def puts(*args)
  Kernel.puts(*args.map { |a| a.is_a?(String) ? a.upcase : a })
end

puts 'hello'

Выход:

HELLO

Поскольку исправление основных классов - грязное дело, давайте посмотрим, сможем ли мы найти более чистый подход. Документация для Kernel.puts гласит:

Эквивалент $stdout.puts(obj, ...)

Это звучит многообещающе: $stdout как глобальная переменная может быть легко изменена. Все, что нам нужно, это объект, который предоставляет собственный метод puts и делегирует любой другой метод исходному стандартному выводу. Вот для чего SimpleDelegator:

class OutputDecorator < SimpleDelegator
  def puts(*args)
    super *args.map { |a| a.is_a?(String) ? a.upcase : a }
  end
end

Давайте попробуем:

puts 'before'

$stdout = OutputDecorator.new($stdout)

puts 'within'

$stdout = $stdout.__getobj__

puts 'after'

Выход:

before
WITHIN
after
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...