claide 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c48ac46a6dd1a3d4b6e9b31d6e7e0a9b4697ecc7
4
- data.tar.gz: aafaf22c16450580a41154a005103b321654fddf
3
+ metadata.gz: cc7d7f59268bec92fc656c3999c983c819b16b74
4
+ data.tar.gz: c1b9b2bce23334941e832dee7aa7f19a7ebb85c0
5
5
  SHA512:
6
- metadata.gz: 9f2a2db1a5e9632761d90bd3486376fe856b6f4012f69391ea093b810c3883cd1f8b9b1b271f42bec2b3b105ba2ef3a14adf1a07ec2fa45f97edb6785f0dedaf
7
- data.tar.gz: ee607530bb899f4a376bcb6a262a9dbf8bd7790cf85a568f99234ad156249f23e38f5f2cd9f364da6c626ef9fba0501c0c8277b9508cce99cc34f835462288cc
6
+ metadata.gz: 171f3513817794fea26279c20f65a9d8cf71ab03edce821e428b86e363ebb75f69de418097c8a67a3415cd755b26f867e7c0b626944cc9c7a590e07a69b944ce
7
+ data.tar.gz: 1949a384b568d4fa1722696827dc66059b3641dc932bdff88ec57721ca48434c15ad7920efbbd632eb8df74f09b1d7373a72b95fce5570aff52f20c0910def64
@@ -1,12 +1,13 @@
1
1
  # Hi, I’m Claide, your command-line tool aide.
2
2
 
3
+ [![Build Status](https://img.shields.io/travis/CocoaPods/CLAide/master.svg?style=flat)](https://travis-ci.org/CocoaPods/CLAide)
4
+ [![Code Climate](https://img.shields.io/codeclimate/github/CocoaPods/CLAide.svg?style=flat)](https://codeclimate.com/github/CocoaPods/CLAide)
5
+ [![Coverage](https://img.shields.io/codeclimate/coverage/github/CocoaPods/CLAide.svg?style=flat)](https://codeclimate.com/github/CocoaPods/CLAide)
6
+
3
7
  I was born out of a need for a _simple_ option and command parser, while still
4
8
  providing an API that allows you to quickly create a full featured command-line
5
9
  interface.
6
10
 
7
- [![Build Status][travis-status]][travis]
8
-
9
-
10
11
  ## Install
11
12
 
12
13
  ```
@@ -4,16 +4,17 @@
4
4
  # {CLAide::InformativeError}
5
5
  #
6
6
  module CLAide
7
-
8
7
  # @return [String]
9
8
  #
10
9
  # CLAide’s version, following [semver](http://semver.org).
11
10
  #
12
- VERSION = '0.5.0'
11
+ VERSION = '0.6.0'
13
12
 
13
+ require 'claide/ansi'
14
14
  require 'claide/argv'
15
15
  require 'claide/command'
16
16
  require 'claide/help'
17
+ require 'claide/helper'
17
18
  require 'claide/informative_error'
18
-
19
+ require 'claide/mixins'
19
20
  end
@@ -0,0 +1,126 @@
1
+ # encoding: utf-8
2
+
3
+ require 'claide/ansi/cursor'
4
+ require 'claide/ansi/graphics'
5
+
6
+ module CLAide
7
+ # Provides support for ANSI Escape sequences
8
+ #
9
+ # For more information see:
10
+ #
11
+ # - http://ascii-table.com/ansi-escape-sequences.php
12
+ # - http://en.wikipedia.org/wiki/ANSI_escape_code
13
+ #
14
+ # This functionality has been inspired and derived from the following gems:
15
+ #
16
+ # - colored
17
+ # - colorize
18
+ #
19
+ class ANSI
20
+ extend Cursor
21
+ extend Graphics
22
+
23
+ class << self
24
+ # @return [Bool] Wether the string mixin should be disabled to return the
25
+ # original string. This method is intended to offer a central location
26
+ # where to disable ANSI logic without needed to implement conditionals
27
+ # across the code base of clients.
28
+ #
29
+ # @example
30
+ #
31
+ # "example".ansi.yellow #=> "\e[33mexample\e[39m"
32
+ # ANSI.disabled = true
33
+ # "example".ansi.yellow #=> "example"
34
+ #
35
+ attr_accessor :disabled
36
+ end
37
+
38
+ # @return [Hash{Symbol => Fixnum}] The text attributes codes by their
39
+ # English name.
40
+ #
41
+ TEXT_ATTRIBUTES = {
42
+ :bold => 1,
43
+ :underline => 4,
44
+ :blink => 5,
45
+ :reverse => 7,
46
+ :hidden => 8
47
+ }
48
+
49
+ # @return [Hash{Symbol => Fixnum}] The codes to disable a text attribute by
50
+ # their name.
51
+ #
52
+ TEXT_DISABLE_ATTRIBUTES = {
53
+ :bold => 21,
54
+ :underline => 24,
55
+ :blink => 25,
56
+ :reverse => 27,
57
+ :hidden => 28
58
+ }
59
+
60
+ # Return [String] The escape sequence to reset the graphics.
61
+ #
62
+ RESET_SEQUENCE = "\e[0m"
63
+
64
+ # @return [Hash{Symbol => Fixnum}] The colors codes by their English name.
65
+ #
66
+ COLORS = {
67
+ :black => 0,
68
+ :red => 1,
69
+ :green => 2,
70
+ :yellow => 3,
71
+ :blue => 4,
72
+ :magenta => 5,
73
+ :cyan => 6,
74
+ :white => 7
75
+ }
76
+
77
+ # Return [String] The escape sequence for the default foreground color.
78
+ #
79
+ DEFAULT_FOREGROUND_COLOR = "\e[39m"
80
+
81
+ # Return [String] The escape sequence for the default background color.
82
+ #
83
+ DEFAULT_BACKGROUND_COLOR = "\e[49m"
84
+
85
+ # @return [Fixnum] The code of a key given the map.
86
+ #
87
+ # @param [Symbol] key
88
+ # The key for which the code is needed.
89
+ #
90
+ # @param [Hash{Symbol => Fixnum}] map
91
+ # A hash which associates each code to each key.
92
+ #
93
+ # @raise If the key is not provided.
94
+ # @raise If the key is not present in the map.
95
+ #
96
+ def self.code_for_key(key, map)
97
+ unless key
98
+ raise ArgumentError, 'A key must be provided'
99
+ end
100
+ code = map[key]
101
+ unless code
102
+ raise ArgumentError, "Unsupported key: `#{key}`"
103
+ end
104
+ code
105
+ end
106
+ end
107
+ end
108
+
109
+ #-- String mixin -------------------------------------------------------------#
110
+
111
+ require 'claide/ansi/string_escaper'
112
+
113
+ class String
114
+ # @return [StringEscaper] An object which provides convenience methods to
115
+ # wrap the receiver in ANSI sequences.
116
+ #
117
+ # @example
118
+ #
119
+ # "example".ansi.yellow #=> "\e[33mexample\e[39m"
120
+ # "example".ansi.on_red #=> "\e[41mexample\e[49m"
121
+ # "example".ansi.bold #=> "\e[1mexample\e[21m"
122
+ #
123
+ def ansi
124
+ CLAide::ANSI::StringEscaper.new(self)
125
+ end
126
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ module CLAide
4
+ class ANSI
5
+ # Provides support for generating escape sequences relative to the position
6
+ # of the cursor and to erase parts of text.
7
+ #
8
+ module Cursor
9
+ # @return [String] The escape sequence to set the cursor at the
10
+ # given line.
11
+ #
12
+ # @param [Fixnum] line
13
+ # The line where to place the cursor.
14
+ #
15
+ # @param [Fixnum] column
16
+ # The column where to place the cursor.
17
+ #
18
+ def self.set_cursor_position(line = 0, column = 0)
19
+ "\e[#{line};#{column}H"
20
+ end
21
+
22
+ # @return [String] The escape sequence to set the cursor at the
23
+ # given line.
24
+ #
25
+ # @param [Fixnum] lines
26
+ # The amount of lines the cursor should be moved to.
27
+ # Negative values indicate up direction and positive ones
28
+ # down direction.
29
+ #
30
+ # @param [Fixnum] columns
31
+ # The amount of columns the cursor should be moved to.
32
+ # Negative values indicate left direction and positive ones
33
+ # right direction.
34
+ #
35
+ def self.move_cursor(lines, columns = 0)
36
+ lines_code = lines < 0 ? 'A' : 'B'
37
+ columns_code = columns > 0 ? 'C' : 'D'
38
+ "\e[#{lines.abs}#{lines_code};#{columns.abs}#{columns_code}"
39
+ end
40
+
41
+ # @return [String] The escape sequence to save the cursor position.
42
+ #
43
+ def self.save_cursor_position
44
+ "\e[s"
45
+ end
46
+
47
+ # @return [String] The escape sequence to restore the cursor to the
48
+ # previously saved position. This sequence also clears all the
49
+ # output after the position.
50
+ #
51
+ def self.restore_cursor_position
52
+ "\e[u"
53
+ end
54
+
55
+ # @return [String] The escape sequence to erase the display.
56
+ #
57
+ def self.erase_display
58
+ "\e[2J"
59
+ end
60
+
61
+ # @return [String] The escape sequence to erase a line form the
62
+ # cursor position to then end.
63
+ #
64
+ def self.erase_line
65
+ "\e[K"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ module CLAide
4
+ class ANSI
5
+ # Provides support for generating escape sequences relative to the graphic
6
+ # mode.
7
+ #
8
+ module Graphics
9
+ # @return [String] The escape sequence for a text attribute.
10
+ #
11
+ # @param [Symbol] key
12
+ # The name of the text attribute.
13
+ #
14
+ def self.text_attribute(key)
15
+ code = ANSI.code_for_key(key, TEXT_ATTRIBUTES)
16
+ graphics_mode(code)
17
+ end
18
+
19
+ # @return [String] The escape sequence for a foreground color.
20
+ #
21
+ # @param [Symbol] key
22
+ # The name of the color.
23
+ #
24
+ def self.foreground_color(key)
25
+ code = ANSI.code_for_key(key, COLORS) + 30
26
+ graphics_mode(code)
27
+ end
28
+
29
+ # @return [String] The escape sequence for a background color.
30
+ #
31
+ # @param [Symbol] key
32
+ # The name of the color.
33
+ #
34
+ def self.background_color(key)
35
+ code = ANSI.code_for_key(key, COLORS) + 40
36
+ graphics_mode(code)
37
+ end
38
+
39
+ # @return [String] The escape sequence for a foreground color using the
40
+ # xterm-256 format.
41
+ #
42
+ # @param [Fixnum] key
43
+ # The value of the color.
44
+ #
45
+ def self.foreground_color_256(color)
46
+ code = [38, 5, color]
47
+ graphics_mode(code)
48
+ end
49
+
50
+ # @return [String] The escape sequence for a background color using the
51
+ # xterm-256 format.
52
+ #
53
+ # @param [Fixnum] key
54
+ # The value of the color.
55
+ #
56
+ def self.background_color_256(color)
57
+ code = [48, 5, color]
58
+ graphics_mode(code)
59
+ end
60
+
61
+ # @return [String] The escape sequence for a single or a list of codes.
62
+ #
63
+ # @param [Fixnum, Array<Fixnum>] codes
64
+ # The code(s).
65
+ #
66
+ def self.graphics_mode(codes)
67
+ codes = Array(codes)
68
+ "\e[#{codes.join(';')}m"
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,81 @@
1
+ require 'claide/ansi'
2
+
3
+ module CLAide
4
+ class ANSI
5
+ # Provides support to wrap strings in ANSI sequences according to the
6
+ # `ANSI.disabled` setting.
7
+ #
8
+ class StringEscaper < String
9
+ # @param [String] The string to wrap.
10
+ #
11
+ def initialize(string)
12
+ super
13
+ end
14
+
15
+ # @return [StringEscaper] Wraps a string in the given ANSI sequences,
16
+ # taking care of handling existing sequences for the same
17
+ # family of attributes (i.e. attributes terminated by the
18
+ # same sequence).
19
+ #
20
+ def wrap_in_ansi_sequence(open, close)
21
+ if ANSI.disabled
22
+ self
23
+ else
24
+ gsub!(close, open)
25
+ insert(0, open).insert(-1, close)
26
+ end
27
+ end
28
+
29
+ # @return [StringEscaper]
30
+ #
31
+ # @param [Array<Symbol>] keys
32
+ # One or more keys corresponding to ANSI codes to apply to the
33
+ # string.
34
+ #
35
+ def apply(*keys)
36
+ keys.flatten.each do |key|
37
+ send(key)
38
+ end
39
+ self
40
+ end
41
+
42
+ ANSI::COLORS.each_key do |key|
43
+ # Defines a method returns a copy of the receiver wrapped in an ANSI
44
+ # sequence for each foreground color (e.g. #blue).
45
+ #
46
+ # The methods handle nesting of ANSI sequences.
47
+ #
48
+ define_method key do
49
+ open = Graphics.foreground_color(key)
50
+ close = ANSI::DEFAULT_FOREGROUND_COLOR
51
+ wrap_in_ansi_sequence(open, close)
52
+ end
53
+
54
+ # Defines a method returns a copy of the receiver wrapped in an ANSI
55
+ # sequence for each background color (e.g. #on_blue).
56
+ #
57
+ # The methods handle nesting of ANSI sequences.
58
+ #
59
+ define_method "on_#{key}" do
60
+ open = Graphics.background_color(key)
61
+ close = ANSI::DEFAULT_BACKGROUND_COLOR
62
+ wrap_in_ansi_sequence(open, close)
63
+ end
64
+ end
65
+
66
+ ANSI::TEXT_ATTRIBUTES.each_key do |key|
67
+ # Defines a method returns a copy of the receiver wrapped in an ANSI
68
+ # sequence for each text attribute (e.g. #bold).
69
+ #
70
+ # The methods handle nesting of ANSI sequences.
71
+ #
72
+ define_method key do
73
+ open = Graphics.text_attribute(key)
74
+ close_code = TEXT_DISABLE_ATTRIBUTES[key]
75
+ close = Graphics.graphics_mode(close_code)
76
+ wrap_in_ansi_sequence(open, close)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,34 +1,42 @@
1
1
  # encoding: utf-8
2
2
 
3
- module CLAide
3
+ require 'claide/argv/parser'
4
4
 
5
+ module CLAide
5
6
  # This class is responsible for parsing the parameters specified by the user,
6
7
  # accessing individual parameters, and keep state by removing handled
7
8
  # parameters.
8
9
  #
9
10
  class ARGV
10
-
11
- # @param [Array<String>] argv
11
+ # @return [ARGV] Coherces an object to the ARGV class if needed.
12
12
  #
13
- # A list of parameters. Each entry is ensured to be a string by calling
14
- # `#to_s` on it.
13
+ # @param [Object] argv
14
+ # The object which should be converted to the ARGV class.
15
15
  #
16
- def initialize(argv)
17
- @entries = self.class.parse(argv)
16
+ def self.coherce(argv)
17
+ if argv.is_a?(ARGV)
18
+ argv
19
+ else
20
+ ARGV.new(argv)
21
+ end
18
22
  end
19
23
 
20
- # @return [Boolean]
24
+ # @param [Array<#to_s>] argv
25
+ # A list of parameters.
21
26
  #
22
- # Returns whether or not there are any remaining unhandled parameters.
27
+ def initialize(argv)
28
+ @entries = Parser.parse(argv)
29
+ end
30
+
31
+ # @return [Boolean] Whether or not there are any remaining unhandled
32
+ # parameters.
23
33
  #
24
34
  def empty?
25
35
  @entries.empty?
26
36
  end
27
37
 
28
- # @return [Array<String>]
29
- #
30
- # A list of the remaining unhandled parameters, in the same format a user
31
- # specifies it in.
38
+ # @return [Array<String>] A list of the remaining unhandled parameters, in
39
+ # the same format a user specifies it in.
32
40
  #
33
41
  # @example
34
42
  #
@@ -49,10 +57,8 @@ module CLAide
49
57
  end
50
58
  end
51
59
 
52
- # @return [Hash]
53
- #
54
- # A hash that consists of the remaining flags and options and their
55
- # values.
60
+ # @return [Hash] A hash that consists of the remaining flags and options
61
+ # and their values.
56
62
  #
57
63
  # @example
58
64
  #
@@ -67,9 +73,7 @@ module CLAide
67
73
  options
68
74
  end
69
75
 
70
- # @return [Array<String>]
71
- #
72
- # A list of the remaining arguments.
76
+ # @return [Array<String>] A list of the remaining arguments.
73
77
  #
74
78
  # @example
75
79
  #
@@ -81,13 +85,10 @@ module CLAide
81
85
  @entries.map { |type, value| value if type == :arg }.compact
82
86
  end
83
87
 
84
- # @return [Array<String>]
85
- #
86
- # A list of the remaining arguments.
87
- #
88
- # @note
88
+ # @return [Array<String>] A list of the remaining arguments.
89
89
  #
90
- # This version also removes the arguments from the remaining parameters.
90
+ # @note This version also removes the arguments from the remaining
91
+ # parameters.
91
92
  #
92
93
  # @example
93
94
  #
@@ -104,13 +105,9 @@ module CLAide
104
105
  arguments
105
106
  end
106
107
 
107
- # @return [String]
108
- #
109
- # The first argument in the remaining parameters.
110
- #
111
- # @note
108
+ # @return [String] The first argument in the remaining parameters.
112
109
  #
113
- # This will remove the argument from the remaining parameters.
110
+ # @note This will remove the argument from the remaining parameters.
114
111
  #
115
112
  # @example
116
113
  #
@@ -126,23 +123,17 @@ module CLAide
126
123
  end
127
124
  end
128
125
 
129
- # @return [Boolean, nil]
126
+ # @return [Boolean, nil] Returns `true` if the flag by the specified `name`
127
+ # is among the remaining parameters and is not negated.
130
128
  #
131
- # Returns `true` if the flag by the specified `name` is among the
132
- # remaining parameters and is not negated.
129
+ # @param [String] name
130
+ # The name of the flag to look for among the remaining parameters.
133
131
  #
134
- # @param [String] name
132
+ # @param [Boolean] default
133
+ # The value that is returned in case the flag is not among the
134
+ # remaining parameters.
135
135
  #
136
- # The name of the flag to look for among the remaining parameters.
137
- #
138
- # @param [Boolean] default
139
- #
140
- # The value that is returned in case the flag is not among the remaining
141
- # parameters.
142
- #
143
- # @note
144
- #
145
- # This will remove the flag from the remaining parameters.
136
+ # @note This will remove the flag from the remaining parameters.
146
137
  #
147
138
  # @example
148
139
  #
@@ -156,23 +147,18 @@ module CLAide
156
147
  delete_entry(:flag, name, default)
157
148
  end
158
149
 
159
- # @return [String, nil]
160
- #
161
- # Returns the value of the option by the specified `name` is among the
162
- # remaining parameters.
150
+ # @return [String, nil] Returns the value of the option by the specified
151
+ # `name` is among the remaining parameters.
163
152
  #
164
- # @param [String] name
153
+ # @param [String] name
154
+ # The name of the option to look for among the remaining
155
+ # parameters.
165
156
  #
166
- # The name of the option to look for among the remaining parameters.
157
+ # @param [String] default
158
+ # The value that is returned in case the option is not among the
159
+ # remaining parameters.
167
160
  #
168
- # @param [String] default
169
- #
170
- # The value that is returned in case the option is not among the
171
- # remaining parameters.
172
- #
173
- # @note
174
- #
175
- # This will remove the option from the remaining parameters.
161
+ # @note This will remove the option from the remaining parameters.
176
162
  #
177
163
  # @example
178
164
  #
@@ -188,8 +174,25 @@ module CLAide
188
174
 
189
175
  private
190
176
 
177
+ # @return [Array<Array<Symbol, String, Array>>] A list of tuples for each
178
+ # non consumed parameter, where the first entry is the `type` and
179
+ # the second entry the actual parsed parameter.
180
+ #
191
181
  attr_reader :entries
192
182
 
183
+ # @return [Bool, String, Nil] Removes an entry from the entries list and
184
+ # returns its value or the default value if the entry was not
185
+ # present.
186
+ #
187
+ # @param [Symbol] requested_type
188
+ # The type of the entry.
189
+ #
190
+ # @param [String] requested_key
191
+ # The key of the entry.
192
+ #
193
+ # @param [Bool, String, Nil] default
194
+ # The value which should be returned if the entry is not present.
195
+ #
193
196
  def delete_entry(requested_type, requested_key, default)
194
197
  result = nil
195
198
  @entries.delete_if do |type, (key, value)|
@@ -200,53 +203,5 @@ module CLAide
200
203
  end
201
204
  result.nil? ? default : result
202
205
  end
203
-
204
- # @return [Array<Array>]
205
- #
206
- # A list of tuples for each parameter, where the first entry is the
207
- # `type` and the second entry the actual parsed parameter.
208
- #
209
- # @example
210
- #
211
- # list = parse(['tea', '--no-milk', '--sweetner=honey'])
212
- # list # => [[:arg, "tea"],
213
- # [:flag, ["milk", false]],
214
- # [:option, ["sweetner", "honey"]]]
215
- #
216
- def self.parse(argv)
217
- entries = []
218
- copy = argv.map(&:to_s)
219
- while x = copy.shift
220
- type = key = value = nil
221
- if is_arg?(x)
222
- # A regular argument (e.g. a command)
223
- type, value = :arg, x
224
- else
225
- key = x[2..-1]
226
- if key.include?('=')
227
- # An option with a value
228
- type = :option
229
- key, value = key.split('=', 2)
230
- else
231
- # A boolean flag
232
- type = :flag
233
- value = true
234
- if key[0,3] == 'no-'
235
- # A negated boolean flag
236
- key = key[3..-1]
237
- value = false
238
- end
239
- end
240
- value = [key, value]
241
- end
242
- entries << [type, value]
243
- end
244
- entries
245
- end
246
-
247
- def self.is_arg?(x)
248
- x[0,2] != '--'
249
- end
250
-
251
206
  end
252
207
  end