clive 0.8.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.md +328 -227
- data/lib/clive.rb +130 -50
- data/lib/clive/argument.rb +170 -0
- data/lib/clive/arguments.rb +139 -0
- data/lib/clive/arguments/parser.rb +210 -0
- data/lib/clive/base.rb +189 -0
- data/lib/clive/command.rb +342 -444
- data/lib/clive/error.rb +66 -0
- data/lib/clive/formatter.rb +57 -141
- data/lib/clive/formatter/colour.rb +37 -0
- data/lib/clive/formatter/plain.rb +172 -0
- data/lib/clive/option.rb +185 -75
- data/lib/clive/option/runner.rb +163 -0
- data/lib/clive/output.rb +141 -16
- data/lib/clive/parser.rb +180 -87
- data/lib/clive/struct_hash.rb +109 -0
- data/lib/clive/type.rb +117 -0
- data/lib/clive/type/definitions.rb +170 -0
- data/lib/clive/type/lookup.rb +23 -0
- data/lib/clive/version.rb +3 -3
- data/spec/clive/a_cli_spec.rb +245 -0
- data/spec/clive/argument_spec.rb +148 -0
- data/spec/clive/arguments/parser_spec.rb +35 -0
- data/spec/clive/arguments_spec.rb +191 -0
- data/spec/clive/command_spec.rb +276 -209
- data/spec/clive/formatter/colour_spec.rb +129 -0
- data/spec/clive/formatter/plain_spec.rb +129 -0
- data/spec/clive/option/runner_spec.rb +92 -0
- data/spec/clive/option_spec.rb +149 -23
- data/spec/clive/output_spec.rb +86 -2
- data/spec/clive/parser_spec.rb +201 -81
- data/spec/clive/struct_hash_spec.rb +82 -0
- data/spec/clive/type/definitions_spec.rb +312 -0
- data/spec/clive/type_spec.rb +107 -0
- data/spec/clive_spec.rb +60 -0
- data/spec/extras/expectations.rb +86 -0
- data/spec/extras/focus.rb +22 -0
- data/spec/helper.rb +35 -0
- metadata +56 -36
- data/lib/clive/bool.rb +0 -67
- data/lib/clive/exceptions.rb +0 -54
- data/lib/clive/flag.rb +0 -199
- data/lib/clive/switch.rb +0 -31
- data/lib/clive/tokens.rb +0 -141
- data/spec/clive/bool_spec.rb +0 -54
- data/spec/clive/flag_spec.rb +0 -117
- data/spec/clive/formatter_spec.rb +0 -108
- data/spec/clive/switch_spec.rb +0 -14
- data/spec/clive/tokens_spec.rb +0 -38
- data/spec/shared_specs.rb +0 -16
- data/spec/spec_helper.rb +0 -12
data/lib/clive/option.rb
CHANGED
@@ -1,100 +1,210 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
1
|
+
class Clive
|
2
|
+
|
3
|
+
# An option saves a value to the state or triggers a block if given when used.
|
4
|
+
# They can have a long, +--opt+, and/or short, +-o+ and can take arguments
|
5
|
+
# which can be constricted by passing various parameters.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# class CLI < Clive
|
8
|
+
# VERSION = '0.0.1'
|
9
|
+
#
|
10
|
+
# desc 'Name of the person'
|
11
|
+
# opt :name, arg: '<first> [<middle>] <last>'
|
12
|
+
#
|
13
|
+
# opt :v, :version do
|
14
|
+
# puts CLI::VERSION
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# You can also have boolean options, created with the +bool+ or +boolean+
|
19
|
+
# method which are simply Options with :boolean set to true. You can pass the
|
20
|
+
# option name as normal to set them to true or prepend +--no-+ to the name to
|
21
|
+
# set them to false.
|
22
|
+
#
|
23
|
+
# class CLI
|
24
|
+
# bool :auto, 'Auto regenerate person on change'
|
25
|
+
# end
|
10
26
|
#
|
11
27
|
class Option
|
12
|
-
|
13
|
-
|
14
|
-
|
28
|
+
|
29
|
+
class InvalidNamesError < Error
|
30
|
+
reason '#1'
|
31
|
+
end
|
32
|
+
|
33
|
+
extend Type::Lookup
|
34
|
+
|
35
|
+
# @return [Array<Symbol>] List of names this Option can be called
|
36
|
+
attr_reader :names
|
37
|
+
# @return [Hash{Symbol=>Object}] Config options passed to {#initialize}
|
38
|
+
# using defaults when not given
|
39
|
+
attr_reader :config
|
40
|
+
# @return [Arguments] List of arguments this Option can take when ran
|
41
|
+
attr_reader :args
|
42
|
+
# @return [String] Description of the Option
|
43
|
+
attr_reader :description
|
44
|
+
|
45
|
+
# Default values to use for +config+. These are also the config options that
|
46
|
+
# an Option takes, see {#initialize} for details.
|
47
|
+
DEFAULTS = {
|
48
|
+
:boolean => false,
|
49
|
+
:group => nil,
|
50
|
+
:head => false,
|
51
|
+
:tail => false,
|
52
|
+
:runner => Clive::Option::Runner
|
53
|
+
}
|
54
|
+
|
55
|
+
# @param names [Array<Symbol>]
|
56
|
+
# Names for this option
|
15
57
|
#
|
16
|
-
#
|
17
|
-
#
|
58
|
+
# @param description [String]
|
59
|
+
# Description of the option.
|
18
60
|
#
|
19
|
-
# @param
|
20
|
-
#
|
61
|
+
# @param config [Hash]
|
62
|
+
# @option config [true, false] :head
|
63
|
+
# If option should be at top of help list
|
64
|
+
# @option config [true, false] :tail
|
65
|
+
# If option should be at bottom of help list
|
66
|
+
# @option config [String] :args
|
67
|
+
# Arguments that the option takes. See {Argument}.
|
68
|
+
# @option config [Type, Array[Type]] :as
|
69
|
+
# The class the argument(s) should be cast to. See {Type}.
|
70
|
+
# @option config [#match, Array[#match]] :match
|
71
|
+
# Regular expression that the argument(s) must match
|
72
|
+
# @option config [#include?, Array[#include?]] :in
|
73
|
+
# Collection that argument(s) must be in
|
74
|
+
# @option config :default
|
75
|
+
# Default value that is used if argument is not given
|
76
|
+
# @option config :group
|
77
|
+
# Name of the group this option belongs to
|
21
78
|
#
|
22
|
-
# @
|
23
|
-
# A description of what the option does.
|
79
|
+
# @example
|
24
80
|
#
|
25
|
-
#
|
81
|
+
# Option.new(
|
82
|
+
# [:N, :new],
|
83
|
+
# "Add a new thing",
|
84
|
+
# {:args => "<dir> [<size>]", :matches => [/^\//], :types => [nil, Integer]}
|
85
|
+
# )
|
26
86
|
#
|
27
|
-
def initialize(names,
|
28
|
-
@names = names.
|
29
|
-
|
87
|
+
def initialize(names=[], description="", config={}, &block)
|
88
|
+
@names = names.sort_by {|i| i.to_s.size }
|
89
|
+
|
90
|
+
# @return [Symbol, nil] Short name from the names (ie. +:a+)
|
91
|
+
def @names.short
|
92
|
+
find {|i| i.to_s.size == 1 }
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [Symbol, nil] Long name from the names (ie. +:abc+)
|
96
|
+
def @names.long
|
97
|
+
find {|i| i.to_s.size > 1 }
|
98
|
+
end
|
99
|
+
|
100
|
+
@description = description
|
30
101
|
@block = block
|
102
|
+
|
103
|
+
@args = Arguments.create( get_subhash(config, Arguments::Parser::KEYS.keys) )
|
104
|
+
@config = DEFAULTS.merge( get_subhash(config, DEFAULTS.keys) || {} )
|
31
105
|
end
|
32
|
-
|
33
|
-
#
|
34
|
-
def
|
35
|
-
@
|
106
|
+
|
107
|
+
# @return [Symbol] The longest name given
|
108
|
+
def name
|
109
|
+
@names.long || @names.short
|
36
110
|
end
|
37
|
-
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
# #=> ['-v', '--verbose']
|
48
|
-
#
|
49
|
-
def names_to_strings(bool=false)
|
50
|
-
r = []
|
51
|
-
@names.each do |i|
|
52
|
-
next if i.nil?
|
53
|
-
if i.length == 1 # short
|
54
|
-
r << "-#{i}"
|
55
|
-
else # long
|
56
|
-
if bool
|
57
|
-
r << "--[no-]#{i}"
|
58
|
-
else
|
59
|
-
r << "--#{i}"
|
60
|
-
end
|
61
|
-
end
|
111
|
+
|
112
|
+
# @return [String] String representaion of the Option
|
113
|
+
def to_s
|
114
|
+
r = ""
|
115
|
+
r << "-#{@names.short}" if @names.short
|
116
|
+
if @names.long
|
117
|
+
r << ", " if @names.short
|
118
|
+
r << "--"
|
119
|
+
r << "[no-]" if @config[:boolean] == true
|
120
|
+
r << @names.long.to_s.gsub('_', '-')
|
62
121
|
end
|
122
|
+
|
63
123
|
r
|
64
124
|
end
|
65
|
-
|
66
|
-
#
|
67
|
-
|
68
|
-
|
125
|
+
|
126
|
+
# @return [String]
|
127
|
+
def inspect
|
128
|
+
"#<#{self.class} #{to_s}>"
|
129
|
+
end
|
130
|
+
|
131
|
+
# @return Whether a block was given.
|
132
|
+
def block?
|
133
|
+
@block != nil
|
134
|
+
end
|
135
|
+
|
136
|
+
# Runs the Option's block with the current state and arguments passed.
|
69
137
|
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
138
|
+
# @param state [Hash] Local state for parser, this may be modified!
|
139
|
+
# @param args [Array] Arguments for the block which is run
|
140
|
+
# @param scope [Command] Scope of the state to use
|
141
|
+
# @return [Hash] the state which may have been modified!
|
142
|
+
def run(state, args=[], scope=nil)
|
143
|
+
mapped_args = if @config[:boolean] == true
|
144
|
+
[[:truth, args.first]]
|
145
|
+
else
|
146
|
+
@args.zip(args).map {|k,v| [k.name, v] }
|
147
|
+
end
|
148
|
+
|
149
|
+
if block?
|
150
|
+
if scope
|
151
|
+
state = @config[:runner]._run(mapped_args, state[scope.name], @block)
|
152
|
+
else
|
153
|
+
state = @config[:runner]._run(mapped_args, state, @block)
|
75
154
|
end
|
155
|
+
else
|
156
|
+
state = set_state(state, args, scope)
|
76
157
|
end
|
77
|
-
|
158
|
+
|
159
|
+
state
|
78
160
|
end
|
79
|
-
|
80
|
-
|
161
|
+
|
162
|
+
include Comparable
|
163
|
+
|
164
|
+
# Compare based on the size of {#name}, makes sure tails go to the bottom
|
165
|
+
# and heads go to the top. If both are head or tail then sorts based on the
|
166
|
+
# names.
|
167
|
+
#
|
168
|
+
# @param other [Option] Option to compare with
|
169
|
+
# @return [Integer] Either -1, 0 or 1
|
81
170
|
def <=>(other)
|
82
|
-
|
171
|
+
if (config[:tail] && !other.config[:tail]) ||
|
172
|
+
(other.config[:head] && !config[:head])
|
173
|
+
1
|
174
|
+
elsif (other.config[:tail] && !config[:tail]) ||
|
175
|
+
(config[:head] && !other.config[:head])
|
176
|
+
-1
|
177
|
+
else
|
178
|
+
self.name.to_s <=> other.name.to_s
|
179
|
+
end
|
83
180
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
181
|
+
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
# Sets a value in the state.
|
87
186
|
#
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
187
|
+
# @param state [#store, #[]]
|
188
|
+
# @param args [Array]
|
189
|
+
# @param scope [Symbol, nil]
|
190
|
+
def set_state(state, args, scope=nil)
|
191
|
+
args = (@args.max <= 1 ? args[0] : args)
|
192
|
+
|
193
|
+
if scope
|
194
|
+
state[scope.name].store [@names.long, @names.short].compact, args
|
195
|
+
else
|
196
|
+
state.store [@names.long, @names.short].compact, args
|
197
|
+
end
|
198
|
+
|
199
|
+
state
|
93
200
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
201
|
+
|
202
|
+
# @param hash [Hash]
|
203
|
+
# @param keys [Array]
|
204
|
+
def get_subhash(hash, keys)
|
205
|
+
Hash[ hash.find_all {|k,v| keys.include?(k) } ]
|
97
206
|
end
|
98
|
-
|
207
|
+
|
99
208
|
end
|
209
|
+
|
100
210
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
class Clive
|
2
|
+
|
3
|
+
# Methods for modifying a state object. Requires that the instance variable
|
4
|
+
# +@state+ exists and that it responds to +#fetch+, +#store+ and +#key?+.
|
5
|
+
module StateActions
|
6
|
+
|
7
|
+
# @param key [Symbol]
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# set :some_key, 1
|
11
|
+
# opt :get_some_key do
|
12
|
+
# puts get(:some_key) #=> 1
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
def get(key)
|
16
|
+
@state.fetch key
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param key [Symbol]
|
20
|
+
# @param value [Object]
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# opt :set_some_key do
|
24
|
+
# set :some_key, 1
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
def set(key, value)
|
28
|
+
@state.store key, value
|
29
|
+
end
|
30
|
+
|
31
|
+
# @overload update(key, method, *args)
|
32
|
+
# Update the value for +key+ using the +method+ which is passed +args+
|
33
|
+
# @param key [Symbol]
|
34
|
+
# @param method [Symbol]
|
35
|
+
# @param args [Object]
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# set :list, []
|
39
|
+
# opt :add, arg: '<item>' do
|
40
|
+
# update :list, :<<, item
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# @overload update(key, &block)
|
44
|
+
# Update the value for +key+ with a block
|
45
|
+
# @param key [Symbol]
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# set :list, []
|
49
|
+
# opt :add, arg: '<item>' do
|
50
|
+
# update(:list) {|l| l << item }
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
def update(*args)
|
54
|
+
if block_given?
|
55
|
+
key = args.first
|
56
|
+
set key, yield(get(key))
|
57
|
+
elsif args.size > 1
|
58
|
+
key, method = args.shift, args.shift
|
59
|
+
set key, get(key).send(method, *args)
|
60
|
+
else
|
61
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param key [Symbol]
|
66
|
+
# @return State has +key+?
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# # test.rb
|
70
|
+
# set :some_key, 1
|
71
|
+
# opt(:has_some_key) { puts has?(:some_key) }
|
72
|
+
# opt(:has_other_key) { puts has?(:other_key) }
|
73
|
+
#
|
74
|
+
# # ./test.rb --has-some-key #=> true
|
75
|
+
# # ./test.rb --has-other-key #=> false
|
76
|
+
#
|
77
|
+
def has?(key)
|
78
|
+
@state.key? key
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class Option
|
84
|
+
|
85
|
+
# Runner is a class which is used for executing blocks given to Options and
|
86
|
+
# Commands. It allows you to inside blocks;
|
87
|
+
# - reference arguments by name (instead of using block params)
|
88
|
+
# - get values from the state hash
|
89
|
+
# - set value to the state hash
|
90
|
+
# - update values in the state hash
|
91
|
+
#
|
92
|
+
# @example Referencing Arguments by Name
|
93
|
+
#
|
94
|
+
# opt :size, args: '<height> <width>', as: [Float, Float] do # no params!
|
95
|
+
# puts "Area = #{height} * #{width} = #{height * width}"
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# @example Getting Values from State
|
99
|
+
#
|
100
|
+
# command :new, arg: '<dir>' do
|
101
|
+
#
|
102
|
+
# opt :type, in: %w(post page blog)
|
103
|
+
#
|
104
|
+
# action do
|
105
|
+
# type = has?(:type) ? get(:type) : 'page'
|
106
|
+
# puts "Creating #{type} in #{dir}!"
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# @example Setting Values to State
|
112
|
+
#
|
113
|
+
# opt :set, arg: '<key> <value>', as: [Symbol, Object] do
|
114
|
+
# set key, value
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# @example Updating Values in State
|
118
|
+
#
|
119
|
+
# opt :modify, arg: '<key> <sym> [<args>]', as: [Symbol, Symbol, Array] do
|
120
|
+
# update key, sym, *args
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
#
|
124
|
+
class Runner
|
125
|
+
class << self
|
126
|
+
|
127
|
+
# @param args [Array[Symbol,Object]]
|
128
|
+
# An array is used because with 1.8.7 a hash has unpredictable
|
129
|
+
# ordering of keys, this means an array is the only way I can be
|
130
|
+
# sure that the arguments are in order.
|
131
|
+
# @param state [Hash{Symbol=>Object}]
|
132
|
+
# @param fn [Proc]
|
133
|
+
def _run(args, state, fn)
|
134
|
+
return unless fn
|
135
|
+
# order of this doesn't matter as it will just be accessed by key
|
136
|
+
@args = Hash[args]
|
137
|
+
@state = state
|
138
|
+
|
139
|
+
if fn.arity > 0
|
140
|
+
# Remember to use the ordered array version
|
141
|
+
instance_exec(*args.map {|i| i.last }, &fn)
|
142
|
+
else
|
143
|
+
instance_exec(&fn)
|
144
|
+
end
|
145
|
+
|
146
|
+
@state
|
147
|
+
end
|
148
|
+
|
149
|
+
include Clive::StateActions
|
150
|
+
|
151
|
+
# Allows arguments passed in to be referenced directly by name.
|
152
|
+
def method_missing(sym, *args)
|
153
|
+
if @args.has_key?(sym)
|
154
|
+
@args[sym]
|
155
|
+
else
|
156
|
+
super
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
data/lib/clive/output.rb
CHANGED
@@ -1,18 +1,101 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
#
|
1
|
+
class Clive
|
2
|
+
module Output extend self
|
3
|
+
|
4
|
+
# If the terminal width can't be determined use a sensible default value
|
5
|
+
TERMINAL_WIDTH = 80
|
6
|
+
|
7
|
+
# @param str [String] String to pad.
|
8
|
+
# @param len [Integer] Length to pad string to.
|
9
|
+
# @param with [String] String to add to +str+.
|
10
|
+
def pad(str, len, with=" ")
|
11
|
+
diff = len - str.clear_colours.size
|
12
|
+
str += with * diff unless diff < 0
|
13
|
+
str
|
14
|
+
end
|
15
|
+
|
16
|
+
# Wraps text. Each line is split by word and then a newline is inserted
|
17
|
+
# when the words exceed the allowed width. Lines after the first will
|
18
|
+
# have +left_margin+ spaces inserted.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
#
|
22
|
+
# Clive::Output.wrap_text("Lorem ipsum dolor sit amet, consectetur
|
23
|
+
# adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
24
|
+
# dolore magna aliqua.", 4, 24)
|
25
|
+
# #=> "Lorem ipsum dolor
|
26
|
+
# # sit amet,
|
27
|
+
# # consectetur
|
28
|
+
# # adipisicing elit,
|
29
|
+
# # sed do eiusmod
|
30
|
+
# # tempor incididunt ut
|
31
|
+
# # labore et dolore
|
32
|
+
# # magna aliqua."
|
33
|
+
#
|
34
|
+
# @param str [String] Text to be wrapped
|
35
|
+
# @param left_margin [Integer] Width of space at left
|
36
|
+
# @param width [Integer] Total width of text, ie. from the left
|
37
|
+
# of the screen
|
38
|
+
def wrap_text(str, left_margin, width)
|
39
|
+
text_width = width - left_margin
|
40
|
+
|
41
|
+
words = str.split(" ")
|
42
|
+
r = [""]
|
43
|
+
i = 0
|
44
|
+
|
45
|
+
words.each do |word|
|
46
|
+
if (r[i] + word).clear_colours.size < text_width
|
47
|
+
r[i] << " " << word
|
48
|
+
else
|
49
|
+
i += 1
|
50
|
+
r[i] = word
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Clean up strings
|
55
|
+
r.map! {|i| i.strip }
|
56
|
+
|
57
|
+
([r[0]] + r[1..-1].map {|i| l_pad(i, left_margin) }).join("\n")
|
58
|
+
end
|
8
59
|
|
9
|
-
|
10
|
-
|
60
|
+
# @return [Integer,nil] Width of terminal window, or +nil+ if it cannot be
|
61
|
+
# determined.
|
62
|
+
# @see https://github.com/cldwalker/hirb/blob/v0.5.0/lib/hirb/util.rb#L61
|
63
|
+
def terminal_width
|
64
|
+
if (ENV['COLUMNS'] =~ /^\d+$/)
|
65
|
+
ENV['COLUMNS'].to_i
|
66
|
+
elsif (RUBY_PLATFORM =~ /java/ || (!STDIN.tty? && ENV['TERM'])) && command_exists?('tput')
|
67
|
+
`tput cols`.to_i
|
68
|
+
elsif STDIN.tty? && command_exists?('stty')
|
69
|
+
# returns 'height width'
|
70
|
+
`stty size`.scan(/\d+/).last.to_i
|
71
|
+
else
|
72
|
+
TERMINAL_WIDTH
|
73
|
+
end
|
74
|
+
rescue
|
75
|
+
TERMINAL_WIDTH
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Determines if a shell command exists by searching for it in ENV['PATH'].
|
82
|
+
# @see https://github.com/cldwalker/hirb/blob/v0.5.0/lib/hirb/util.rb#L55
|
83
|
+
def command_exists?(command)
|
84
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).any? {|d| File.exists? File.join(d, command) }
|
85
|
+
end
|
86
|
+
|
87
|
+
# Same as #pad but adds to the left of +str+.
|
88
|
+
#
|
89
|
+
# @param str [String] String to pad.
|
90
|
+
# @param margin [Integer] Amount of +with+s to add to the left of +str+.
|
91
|
+
# @param with [String] String to pad with.
|
92
|
+
def l_pad(str, margin, with=" ")
|
93
|
+
(with * margin) + str
|
94
|
+
end
|
11
95
|
|
12
96
|
end
|
13
97
|
end
|
14
98
|
|
15
|
-
# Monkey patches for colour
|
16
99
|
class String
|
17
100
|
|
18
101
|
# @example
|
@@ -31,8 +114,28 @@ class String
|
|
31
114
|
#
|
32
115
|
# puts "combo".blue.bold.underline.blink
|
33
116
|
#
|
117
|
+
# @param code
|
118
|
+
# Colour code, see http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
119
|
+
#
|
34
120
|
def colour(code)
|
35
|
-
"#{code}#{self}
|
121
|
+
r = "\e[#{code}m#{self}"
|
122
|
+
r << "\e[0m" unless self[-4..-1] == "\e[0m"
|
123
|
+
r
|
124
|
+
end
|
125
|
+
|
126
|
+
# Like #colour but modifies the string object.
|
127
|
+
def colour!(code)
|
128
|
+
replace self.colour(code)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Remove any colour codes from a string.
|
132
|
+
def clear_colours
|
133
|
+
gsub /\e\[?\d\d{0,2}m/, ''
|
134
|
+
end
|
135
|
+
|
136
|
+
# Same as #clear_colours, but modifies string.
|
137
|
+
def clear_colours!
|
138
|
+
gsub! /\e\[?\d\d{0,2}m/, ''
|
36
139
|
end
|
37
140
|
|
38
141
|
COLOURS = {
|
@@ -55,19 +158,32 @@ class String
|
|
55
158
|
|
56
159
|
ATTRIBUTES.each do |name, code|
|
57
160
|
define_method name do
|
58
|
-
colour
|
161
|
+
colour code
|
162
|
+
end
|
163
|
+
|
164
|
+
define_method "#{name}!" do
|
165
|
+
colour! code
|
59
166
|
end
|
60
167
|
end
|
61
168
|
|
62
169
|
COLOURS.each do |name, code|
|
63
170
|
define_method name do
|
64
|
-
colour
|
171
|
+
colour "3#{code}"
|
172
|
+
end
|
173
|
+
|
174
|
+
define_method "#{name}!" do
|
175
|
+
colour! "3#{code}"
|
65
176
|
end
|
66
177
|
|
67
178
|
define_method "#{name}_bg" do
|
68
|
-
colour
|
179
|
+
colour "4#{code}"
|
69
180
|
end
|
70
181
|
|
182
|
+
define_method "#{name}_bg!" do
|
183
|
+
colour! "4#{code}"
|
184
|
+
end
|
185
|
+
|
186
|
+
|
71
187
|
# Change name to grey instead of l_black
|
72
188
|
l_name = "l_#{name}"
|
73
189
|
if name == "black"
|
@@ -75,12 +191,21 @@ class String
|
|
75
191
|
end
|
76
192
|
|
77
193
|
define_method "#{l_name}" do
|
78
|
-
colour
|
194
|
+
colour "9#{code}"
|
195
|
+
end
|
196
|
+
|
197
|
+
define_method "#{l_name}!" do
|
198
|
+
colour! "9#{code}"
|
79
199
|
end
|
80
200
|
|
81
201
|
define_method "#{l_name}_bg" do
|
82
|
-
colour
|
202
|
+
colour "10#{code}"
|
203
|
+
end
|
204
|
+
|
205
|
+
define_method "#{l_name}_bg!" do
|
206
|
+
colour! "10#{code}"
|
83
207
|
end
|
84
208
|
|
85
209
|
end
|
210
|
+
|
86
211
|
end
|