toys-core 0.6.1 → 0.7.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
  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