claide 0.5.0 → 0.6.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.
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