toys-core 0.6.1 → 0.7.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
  SHA256:
3
- metadata.gz: ac33c1214b7ae27c8ecea97837cd1ba186dfc3715db69ef52ef9dc24e56f8064
4
- data.tar.gz: 9e8cc340e38cd320893cf9ee47c31c138ea42f9850c8efebb315a9e60182d791
3
+ metadata.gz: 9266b9c87907b8844cd20529fd8102391737845d2e85cecc6674d6657a9d0d27
4
+ data.tar.gz: da4bf58af8136859679ee2d8123e649310af0b82dbe896730af081d4e18c2c5d
5
5
  SHA512:
6
- metadata.gz: e77ed0de1cc6ee14300c4787c334f3ecd97e338d16df4e9004b303a618163b6cd07709cf1d956e9278b9e52624105cdfd87010c55d111580a164a9a6cddf9b4f
7
- data.tar.gz: 02521cfe60b37bb36c7a5550a2c33a42e4de6f74350fa3b852c333642fc7aa0e7e7b4523a94219236f1523ad7a9c3f5297a2467b2a03b5337234fb059eac730c
6
+ metadata.gz: 511416609364d830a23c27279ddcd545182cbca9fc0fc2727f832c75d4cfedaab0e4faa2d23456c880d202c4fb18e95239948bfbbdbe46035ccc008d0d84996b
7
+ data.tar.gz: '075912df93384641e0c7b43807571d8ac4873af0906f022265db1483b66258fea88e7026d0a35c4716a5f96ac237ef9965b53398cdcf31d7254c2b5e2fb85895'
@@ -1,5 +1,11 @@
1
1
  # Release History
2
2
 
3
+ ### 0.7.0 / 2019-01-23
4
+
5
+ * ADDED: Flag groups, which enforce policies around which flags are required.
6
+ * CHANGED: Flags within a group are sorted in help screens.
7
+ * CHANGED: Canonical flag within a flag definition is now the first rather than the last.
8
+
3
9
  ### 0.6.1 / 2019-01-07
4
10
 
5
11
  * FIXED: The presence of aliases caused subtool listing to crash.
@@ -52,11 +52,6 @@ module Toys
52
52
  #
53
53
  module StandardMixins; end
54
54
 
55
- ##
56
- # Namespace for standard middleware classes.
57
- #
58
- module StandardMiddleware; end
59
-
60
55
  ##
61
56
  # Namespace for common utility classes.
62
57
  #
@@ -69,10 +64,12 @@ require "toys/definition/acceptor"
69
64
  require "toys/definition/alias"
70
65
  require "toys/definition/arg"
71
66
  require "toys/definition/flag"
67
+ require "toys/definition/flag_group"
72
68
  require "toys/definition/source_info"
73
69
  require "toys/definition/tool"
74
70
  require "toys/dsl/arg"
75
71
  require "toys/dsl/flag"
72
+ require "toys/dsl/flag_group"
76
73
  require "toys/dsl/tool"
77
74
  require "toys/errors"
78
75
  require "toys/input_file"
@@ -80,6 +77,7 @@ require "toys/loader"
80
77
  require "toys/middleware"
81
78
  require "toys/mixin"
82
79
  require "toys/runner"
80
+ require "toys/standard_middleware"
83
81
  require "toys/template"
84
82
  require "toys/tool"
85
83
  require "toys/utils/exec"
@@ -34,5 +34,5 @@ module Toys
34
34
  # Current version of Toys core
35
35
  # @return [String]
36
36
  #
37
- CORE_VERSION = "0.6.1"
37
+ CORE_VERSION = "0.7.0"
38
38
  end
@@ -41,24 +41,24 @@ module Toys
41
41
  #
42
42
  def initialize(str)
43
43
  case str
44
- when /^(-[\?\w])$/
45
- setup(str, [$1], $1, "-", nil, nil, nil, nil)
46
- when /^(-[\?\w])( ?)\[(\w+)\]$/
47
- setup(str, [$1], $1, "-", :value, :optional, $2, $3)
48
- when /^(-[\?\w])\[( )(\w+)\]$/
49
- setup(str, [$1], $1, "-", :value, :optional, $2, $3)
50
- when /^(-[\?\w])( ?)(\w+)$/
51
- setup(str, [$1], $1, "-", :value, :required, $2, $3)
44
+ when /^(-([\?\w]))$/
45
+ setup(str, [$1], $1, $2, "-", nil, nil, nil, nil)
46
+ when /^(-([\?\w]))( ?)\[(\w+)\]$/
47
+ setup(str, [$1], $1, $2, "-", :value, :optional, $3, $4)
48
+ when /^(-([\?\w]))\[( )(\w+)\]$/
49
+ setup(str, [$1], $1, $2, "-", :value, :optional, $3, $4)
50
+ when /^(-([\?\w]))( ?)(\w+)$/
51
+ setup(str, [$1], $1, $2, "-", :value, :required, $3, $4)
52
52
  when /^--\[no-\](\w[\?\w-]*)$/
53
- setup(str, ["--#{$1}", "--no-#{$1}"], str, "--", :boolean, nil, nil, nil)
54
- when /^(--\w[\?\w-]*)$/
55
- setup(str, [$1], $1, "--", nil, nil, nil, nil)
56
- when /^(--\w[\?\w-]*)([= ])\[(\w+)\]$/
57
- setup(str, [$1], $1, "--", :value, :optional, $2, $3)
58
- when /^(--\w[\?\w-]*)\[([= ])(\w+)\]$/
59
- setup(str, [$1], $1, "--", :value, :optional, $2, $3)
60
- when /^(--\w[\?\w-]*)([= ])(\w+)$/
61
- setup(str, [$1], $1, "--", :value, :required, $2, $3)
53
+ setup(str, ["--#{$1}", "--no-#{$1}"], str, $1, "--", :boolean, nil, nil, nil)
54
+ when /^(--(\w[\?\w-]*))$/
55
+ setup(str, [$1], $1, $2, "--", nil, nil, nil, nil)
56
+ when /^(--(\w[\?\w-]*))([= ])\[(\w+)\]$/
57
+ setup(str, [$1], $1, $2, "--", :value, :optional, $3, $4)
58
+ when /^(--(\w[\?\w-]*))\[([= ])(\w+)\]$/
59
+ setup(str, [$1], $1, $2, "--", :value, :optional, $3, $4)
60
+ when /^(--(\w[\?\w-]*))([= ])(\w+)$/
61
+ setup(str, [$1], $1, $2, "--", :value, :required, $3, $4)
62
62
  else
63
63
  raise ToolDefinitionError, "Illegal flag: #{str.inspect}"
64
64
  end
@@ -67,6 +67,7 @@ module Toys
67
67
  attr_reader :original_str
68
68
  attr_reader :flags
69
69
  attr_reader :str_without_value
70
+ attr_reader :sort_str
70
71
  attr_reader :flag_style
71
72
  attr_reader :flag_type
72
73
  attr_reader :value_type
@@ -91,11 +92,12 @@ module Toys
91
92
 
92
93
  private
93
94
 
94
- def setup(original_str, flags, str_without_value, flag_style, flag_type, value_type,
95
- value_delim, value_label)
95
+ def setup(original_str, flags, str_without_value, sort_str, flag_style, flag_type,
96
+ value_type, value_delim, value_label)
96
97
  @original_str = original_str
97
98
  @flags = flags
98
99
  @str_without_value = str_without_value
100
+ @sort_str = sort_str
99
101
  @flag_style = flag_style
100
102
  @flag_type = flag_type
101
103
  @value_type = value_type
@@ -120,7 +122,9 @@ module Toys
120
122
  # Create a Flag definition
121
123
  # @private
122
124
  #
123
- def initialize(key, flags, used_flags, report_collisions, accept, handler, default)
125
+ def initialize(key, flags, used_flags, report_collisions, accept, handler,
126
+ default, display_name, group)
127
+ @group = group
124
128
  @key = key
125
129
  @flag_syntax = flags.map { |s| FlagSyntax.new(s) }
126
130
  @accept = accept
@@ -133,8 +137,15 @@ module Toys
133
137
  create_default_flag_if_needed(needs_val)
134
138
  remove_used_flags(used_flags, report_collisions)
135
139
  canonicalize(needs_val)
140
+ summarize(display_name)
136
141
  end
137
142
 
143
+ ##
144
+ # Returns the flag group containing this flag
145
+ # @return [Toys::Definition::FlagGroup]
146
+ #
147
+ attr_reader :group
148
+
138
149
  ##
139
150
  # Returns the key.
140
151
  # @return [Symbol]
@@ -205,6 +216,18 @@ module Toys
205
216
  #
206
217
  attr_reader :value_delim
207
218
 
219
+ ##
220
+ # Returns the display name of this flag.
221
+ # @return [String]
222
+ #
223
+ attr_reader :display_name
224
+
225
+ ##
226
+ # Returns a string that can be used to sort this flag
227
+ # @return [String]
228
+ #
229
+ attr_reader :sort_str
230
+
208
231
  ##
209
232
  # Returns an array of FlagSyntax including only single-dash flags
210
233
  # @return [Array<FlagSyntax>]
@@ -302,10 +325,10 @@ module Toys
302
325
  @value_type = nil
303
326
  @value_label = needs_val ? "VALUE" : nil
304
327
  @value_delim = " "
305
- single_flag_syntax.each do |flag|
328
+ single_flag_syntax.reverse_each do |flag|
306
329
  analyze_flag_syntax(flag)
307
330
  end
308
- double_flag_syntax.each do |flag|
331
+ double_flag_syntax.reverse_each do |flag|
309
332
  analyze_flag_syntax(flag)
310
333
  end
311
334
  @flag_type ||= :boolean
@@ -330,6 +353,18 @@ module Toys
330
353
  @value_label = flag.value_label
331
354
  @value_delim = flag.value_delim
332
355
  end
356
+
357
+ def summarize(name)
358
+ @display_name =
359
+ name ||
360
+ double_flag_syntax.first&.canonical_str ||
361
+ single_flag_syntax.first&.canonical_str ||
362
+ key.to_s
363
+ @sort_str =
364
+ double_flag_syntax.first&.sort_str ||
365
+ single_flag_syntax.first&.sort_str ||
366
+ ""
367
+ end
333
368
  end
334
369
  end
335
370
  end
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Daniel Azuma
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are met:
9
+ #
10
+ # * Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ # * Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ # * Neither the name of the copyright holder, nor the names of any other
16
+ # contributors to this software, may be used to endorse or promote products
17
+ # derived from this software without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ # POSSIBILITY OF SUCH DAMAGE.
30
+ ;
31
+
32
+ module Toys
33
+ module Definition
34
+ ##
35
+ # Representation of a group of flags with the same requirement settings.
36
+ #
37
+ class FlagGroup
38
+ ##
39
+ # Create a flag group.
40
+ # @private
41
+ #
42
+ def initialize(name, desc, long_desc)
43
+ @name = name
44
+ @desc = Utils::WrappableString.make(desc || default_desc)
45
+ @long_desc = Utils::WrappableString.make_array(long_desc || default_long_desc)
46
+ @flag_definitions = []
47
+ end
48
+
49
+ ##
50
+ # Returns the symbolic name for this group
51
+ # @return [String,Symbol,nil]
52
+ #
53
+ attr_reader :name
54
+
55
+ ##
56
+ # Returns the short description string.
57
+ # @return [Toys::Utils::WrappableString]
58
+ #
59
+ attr_reader :desc
60
+
61
+ ##
62
+ # Returns the long description strings as an array.
63
+ # @return [Array<Toys::Utils::WrappableString>]
64
+ #
65
+ attr_reader :long_desc
66
+
67
+ ##
68
+ # Returns an array of flags that are in this group.
69
+ # Do not modify the returned array.
70
+ # @return [Array<Toys::Definition::Flag>]
71
+ #
72
+ attr_reader :flag_definitions
73
+
74
+ ##
75
+ # Returns true if this group is empty
76
+ # @return [Boolean]
77
+ #
78
+ def empty?
79
+ flag_definitions.empty?
80
+ end
81
+
82
+ ## @private
83
+ def <<(flag)
84
+ flag_definitions << flag
85
+ end
86
+
87
+ ## @private
88
+ def default_desc
89
+ "Flags"
90
+ end
91
+
92
+ ## @private
93
+ def default_long_desc
94
+ nil
95
+ end
96
+
97
+ ## @private
98
+ def validation_error(_seen)
99
+ nil
100
+ end
101
+
102
+ ##
103
+ # A FlagGroup containing all required flags
104
+ #
105
+ class Required < FlagGroup
106
+ ## @private
107
+ def validation_error(seen)
108
+ flag_definitions.each do |flag|
109
+ unless seen.include?(flag.key)
110
+ return "Flag \"#{flag.display_name}\" is required"
111
+ end
112
+ end
113
+ nil
114
+ end
115
+
116
+ ## @private
117
+ def default_desc
118
+ "Required Flags"
119
+ end
120
+
121
+ ## @private
122
+ def default_long_desc
123
+ "These flags are required."
124
+ end
125
+ end
126
+
127
+ ##
128
+ # A FlagGroup containing all optional flags
129
+ #
130
+ class Optional < FlagGroup
131
+ end
132
+
133
+ ##
134
+ # A FlagGroup in which exactly one flag must be set
135
+ #
136
+ class ExactlyOne < FlagGroup
137
+ ## @private
138
+ def validation_error(seen)
139
+ set_flag = nil
140
+ flag_definitions.each do |flag|
141
+ if seen.include?(flag.key)
142
+ if set_flag
143
+ return "Exactly one out of group \"#{desc}\" is required, but both" \
144
+ " \"#{set_flag.display_name}\" and \"#{flag.display_name}\" were set"
145
+ else
146
+ set_flag = flag
147
+ end
148
+ end
149
+ end
150
+ return "Exactly one out of group \"#{desc}\" is required" unless set_flag
151
+ nil
152
+ end
153
+
154
+ ## @private
155
+ def default_long_desc
156
+ "Exactly one of these flags must be set."
157
+ end
158
+ end
159
+
160
+ ##
161
+ # A FlagGroup in which at most one flag must be set
162
+ #
163
+ class AtMostOne < FlagGroup
164
+ ## @private
165
+ def validation_error(seen)
166
+ set_flag = nil
167
+ flag_definitions.each do |flag|
168
+ if seen.include?(flag.key)
169
+ if set_flag
170
+ return "At most one out of group \"#{desc}\" is required, but both" \
171
+ " \"#{set_flag.display_name}\" and \"#{flag.display_name}\" were set"
172
+ else
173
+ set_flag = flag
174
+ end
175
+ end
176
+ end
177
+ nil
178
+ end
179
+
180
+ ## @private
181
+ def default_long_desc
182
+ "At most one of these flags must be set."
183
+ end
184
+ end
185
+
186
+ ##
187
+ # A FlagGroup in which at least one flag must be set
188
+ #
189
+ class AtLeastOne < FlagGroup
190
+ ## @private
191
+ def validation_error(seen)
192
+ flag_definitions.each do |flag|
193
+ return nil if seen.include?(flag.key)
194
+ end
195
+ "At least one out of group \"#{desc}\" is required"
196
+ end
197
+
198
+ ## @private
199
+ def default_long_desc
200
+ "At least one of these flags must be set."
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -101,6 +101,10 @@ module Toys
101
101
  @used_flags = []
102
102
  @initializers = []
103
103
 
104
+ default_flag_group = Definition::FlagGroup.new(nil, nil, nil)
105
+ @flag_groups = [default_flag_group]
106
+ @flag_group_names = {nil => default_flag_group}
107
+
104
108
  @flag_definitions = []
105
109
  @required_arg_definitions = []
106
110
  @optional_arg_definitions = []
@@ -142,6 +146,12 @@ module Toys
142
146
  #
143
147
  attr_reader :long_desc
144
148
 
149
+ ##
150
+ # Return a list of all defined flag groups, in order.
151
+ # @return [Array<Toys::Definition::FlagGroup>]
152
+ #
153
+ attr_reader :flag_groups
154
+
145
155
  ##
146
156
  # Return a list of all defined flags.
147
157
  # @return [Array<Toys::Definition::Flag>]
@@ -491,6 +501,47 @@ module Toys
491
501
  self
492
502
  end
493
503
 
504
+ ##
505
+ # Add a flag group to the group list.
506
+ #
507
+ # @param [Symbol] type The type of group. Allowed values: `:required`,
508
+ # `:optional`, `:exactly_one`, `:at_most_one`, `:at_least_one`.
509
+ # Default is `:optional`.
510
+ # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
511
+ # description for the group. See {Toys::Definition::Tool#desc=} for a
512
+ # description of allowed formats. Defaults to `"Flags"`.
513
+ # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
514
+ # Long description for the flag group. See
515
+ # {Toys::Definition::Tool#long_desc=} for a description of allowed
516
+ # formats. Defaults to the empty array.
517
+ # @param [String,Symbol,nil] name The name of the group, or nil for no
518
+ # name.
519
+ # @param [Boolean] report_collisions If `true`, raise an exception if a
520
+ # the given name is already taken. If `false`, ignore. Default is
521
+ # `true`.
522
+ # @param [Boolean] prepend If `true`, prepend rather than append the
523
+ # group to the list. Default is `false`.
524
+ #
525
+ def add_flag_group(type: :optional, desc: nil, long_desc: nil,
526
+ name: nil, report_collisions: true, prepend: false)
527
+ if !name.nil? && @flag_group_names.key?(name)
528
+ return self unless report_collisions
529
+ raise ToolDefinitionError, "Flag group #{name} already exists"
530
+ end
531
+ unless type.is_a?(::Class)
532
+ type = Utils::ModuleLookup.to_module_name(type)
533
+ type = Definition::FlagGroup.const_get(type)
534
+ end
535
+ group = type.new(name, desc, long_desc)
536
+ @flag_group_names[name] = group unless name.nil?
537
+ if prepend
538
+ @flag_groups.unshift(group)
539
+ else
540
+ @flag_groups.push(group)
541
+ end
542
+ self
543
+ end
544
+
494
545
  ##
495
546
  # Add a flag to the current tool. Each flag must specify a key which
496
547
  # the script may use to obtain the flag value from the context.
@@ -514,6 +565,9 @@ module Toys
514
565
  # @param [Boolean] report_collisions Raise an exception if a flag is
515
566
  # requested that is already in use or marked as disabled. Default is
516
567
  # true.
568
+ # @param [Toys::Definition::FlagGroup,String,Symbol,nil] group Group for
569
+ # this flag. You may provide a group name, a FlagGroup object, or
570
+ # `nil` which denotes the default group.
517
571
  # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
518
572
  # description for the flag. See {Toys::Definition::Tool#desc=} for a
519
573
  # description of allowed formats. Defaults to the empty string.
@@ -521,18 +575,28 @@ module Toys
521
575
  # Long description for the flag. See
522
576
  # {Toys::Definition::Tool#long_desc=} for a description of allowed
523
577
  # formats. Defaults to the empty array.
578
+ # @param [String] display_name A display name for this flag, used in help
579
+ # text and error messages.
524
580
  #
525
581
  def add_flag(key, flags = [],
526
582
  accept: nil, default: nil, handler: nil,
527
- report_collisions: true,
528
- desc: nil, long_desc: nil)
583
+ report_collisions: true, group: nil,
584
+ desc: nil, long_desc: nil, display_name: nil)
585
+ unless group.is_a?(Definition::FlagGroup)
586
+ group_name = group
587
+ group = @flag_group_names[group_name]
588
+ raise ToolDefinitionError, "No such flag group: #{group_name.inspect}" if group.nil?
589
+ end
529
590
  check_definition_state(is_arg: true)
530
591
  accept = resolve_acceptor(accept)
531
592
  flag_def = Definition::Flag.new(key, flags, @used_flags, report_collisions,
532
- accept, handler, default)
593
+ accept, handler, default, display_name, group)
533
594
  flag_def.desc = desc if desc
534
595
  flag_def.long_desc = long_desc if long_desc
535
- @flag_definitions << flag_def if flag_def.active?
596
+ if flag_def.active?
597
+ @flag_definitions << flag_def
598
+ group << flag_def
599
+ end
536
600
  @default_data[key] = default
537
601
  self
538
602
  end
@@ -732,6 +796,9 @@ module Toys
732
796
  end
733
797
  config_proc.call
734
798
  end
799
+ flag_groups.each do |flag_group|
800
+ flag_group.flag_definitions.sort_by!(&:sort_str)
801
+ end
735
802
  @definition_finished = true
736
803
  end
737
804
  self