Class: Col::Formatter

Inherits:
Object
  • Object
show all
Defined in:
lib/col.rb

Overview

————————————————————————— #

Instance Method Summary collapse

Constructor Details

#initialize(strings, *spec) ⇒ Formatter

Returns a new instance of Formatter.



90
91
92
93
94
# File 'lib/col.rb', line 90

def initialize(strings, *spec)
  check_correct_number_of_arguments(strings, *spec)
  @strings = strings
  @format_spec = normalise_format_spec(*spec)
end

Instance Method Details

#check_correct_number_of_arguments(strings, *spec) ⇒ Object

In general, there should be the same number of arguments as there are strings:

Col["one"].fmt( :b )
Col["one", "two"].fmt( [:red, :on_white], [:bold, :negative] )
Col["one", "two", "three"].fmt( :yellow, [:green, :bold], :italic )
Col["one", "two", "three", "four"].fmt(:rb, :y, :cb, :m)
Col["one", "two", "three", "four"].fmt "rb,y,cb,m"

As a special case, if there is only one string, it can have any number of arguments:

Col["string"].fmt( :yellow, :bold, :italic, :blink, :negative, :on_magenta )

If the number of arguments is incorrect, a Col::Error is thrown.



135
136
137
138
139
140
141
142
143
# File 'lib/col.rb', line 135

def check_correct_number_of_arguments(strings, *spec)
  nargs = spec.size
  if nargs == 1 and spec.first.is_a? String
    nargs = spec.first.split(/,/).size
  end
  if strings.size > 1 and nargs != strings.size
    raise Col::Error, "incorrect number of arguments: #{render(spec)}"
  end
end

#decorated_string(string, spec) ⇒ Object

e.g.

string = "hello"
spec = [:yellow, :bold, :on_red]
result = "hello".send(:yellow).send(:bold).send(:on_red)

Raises:



112
113
114
115
116
# File 'lib/col.rb', line 112

def decorated_string(string, spec)
  raise Col::Error unless string.is_a? String and spec.is_a? Array \
                      and spec.all? { |e| e.is_a? Symbol }
  spec.inject(string) { |str, symbol| Term::ANSIColor.send(symbol, str) }
end

#extract(string) ⇒ Object

Extracts color, style and background color.

"gbow" -> ["g", "b", "ow"]
"g"    -> ["g", nil, nil]
"_b"   -> [nil, "b", nil]


258
259
260
261
262
263
264
265
# File 'lib/col.rb', line 258

def extract(string)
  string += "    "
  color, style, backg = /^(.)(.)(..)/.match(string).captures
  color = nil if [' ', '_'].include? color
  style = nil if [' ', '_'].include? style
  backg = nil if ['  ', '__'].include? backg
  [color, style, backg]
end

#normalise_array(array) ⇒ Object

Input: array of symbols Result: array of symbols, each of which is a legitimate ANSIColor method Note: underscores and nil items are removed from the array



214
215
216
217
218
219
220
221
222
# File 'lib/col.rb', line 214

def normalise_array(array)
  array.reject! { |x| x.nil? or x == :_ or x == '_' }
  invalid_items = array.select { |item| not Col::DB.method? item }
  case invalid_items.size
  when 0 then return array
  when 1 then raise Col::Error, "Invalid item: #{invalid_items.first.inspect}"
  else        raise Col::Error, "Invalid items: #{invalid_items.inspect}"
  end
end

#normalise_format_spec(*spec) ⇒ Object

Each spec in the following groups is equivalent. The last one is normalised.

[ :rb ]
[ "rb" ]
[ [:red, :bold] ]

[ :rb, :_n, :y ]
[ "rb", "_n", "y" ]
[ [:red, :bold], [:negative], [:yellow] ]

[ [:green, :concealed], :blue, :bold ]
[ [:green, :concealed], [:blue], [:bold] ]

[ "rb,y,_i,g_ow" ]
[ "rb", "y", "_i", "g_ow" ]
[ [:red, :bold], [:yellow], [:italic], [:green, :on_white] ]

spec is definitely an array because it was gathered like this:

def fmt(*spec)

“Normalised” means an array with one element for each string. Each element is itself an array of all the properties that apply to that string.



171
172
173
174
175
176
177
178
179
180
181
# File 'lib/col.rb', line 171

def normalise_format_spec(*spec)
  if spec.size == 1 and spec.first.is_a? String and spec.first.index(',')
                                                  # ^^^ "rb,y,_n"
    spec = spec.first.split(',')                  # ['rb', 'y', '_n']
    normalise_format_spec(*spec)
  else
    # We have an array of items.  We need to treat each item individually and
    # put the items together.  We remove nil elements.
    spec.map { |item| normalise_item(item) }.compact
  end
end

#normalise_item(item) ⇒ Object

Examples

Item               Normalised item
:r                 [:red]
"r"                [:red]
:red               [:red]
[:red]             [:red]
"rb"               [:red, :bold]
:rb                [:red, :bold]
[:red, :bold]      [:red, :bold]
:b                 [:blue]
:_b                [:bold]
:__b               error
:__ob              [:on_blue]
"gsow"             [:green, :strikethrough, :on_white]
"_noB"             [:negative, :on_black]
:_                 []
[:_]               []
[:_, :_]           []
  (etc.)


202
203
204
205
206
207
208
209
# File 'lib/col.rb', line 202

def normalise_item(item)
  case item
  when Symbol then normalise_string(item.to_s)
  when Array  then normalise_array(item)
  when String then normalise_string(item)
  else             raise Col::Error, "Invalid item type: #{item.class}"
  end
end

#normalise_string(string) ⇒ Object

Examples

Input           Output
 r               [:red]
 b               [:blue]
 rb              [:red, :bold]
 red             [:red]
 bold            [:bold]
 gsow            [:green, :strikethrough, :on_white]
 _b              [:bold]
 __ob            [:on_blue]
 _               []


235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/col.rb', line 235

def normalise_string(string)
  # Is it already one of the methods?  If so, easy.  If not, split and parse.
  if string == "_"
    []
  elsif Col::DB.method? string
    [ string.intern ]
  elsif (1..4).include? string.size         # say: "g", "gb", "gbow"
    color, style, backg = extract(string)
    color = Col::DB.color(color)            # 'g'  -> :green
    style = Col::DB.style(style)            # 'b'  -> :bold
    backg = Col::DB.background(backg)       # 'ow' -> :on_white
    [ color, style, backg ].compact         # remove nil elements
  else
    raise Col::Error, "Invalid item: #{string.inspect}"
  end
rescue Col::Error => e
  raise Col::Error, "Invalid item: #{string.inspect} (#{e.message})"
end

#render(spec) ⇒ Object



267
268
269
# File 'lib/col.rb', line 267

def render(spec)
  ( (spec.size == 1) ? spec.first : spec ).inspect
end

#resultObject



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/col.rb', line 96

def result
  unless @strings.size == @format_spec.size
    raise Col::Error, "mismatching strings and specs"
  end
  result = String.new
  @strings.zip(@format_spec).each do |string, spec|
    d = decorated_string(string, spec)
    result << d
  end
  result
end