clive 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|