toys 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -30,19 +30,45 @@
30
30
  module Toys
31
31
  module Utils
32
32
  ##
33
- # Helper that generates usage text
33
+ # A helper class that generates usage documentation for a tool.
34
+ #
35
+ # This class generates full usage documentation, including description,
36
+ # switches, and arguments. It is used by middleware that implements help
37
+ # and related options.
34
38
  #
35
39
  class Usage
40
+ ##
41
+ # Create a usage helper given an execution context.
42
+ #
43
+ # @param [Toys::Context] context The current execution context.
44
+ # @return [Toys::Utils::Usage]
45
+ #
36
46
  def self.from_context(context)
37
47
  new(context[Context::TOOL], context[Context::BINARY_NAME], context[Context::LOADER])
38
48
  end
39
49
 
50
+ ##
51
+ # Create a usage helper.
52
+ #
53
+ # @param [Toys::Tool] tool The tool for which to generate documentation.
54
+ # @param [String] binary_name The name of the binary. e.g. `"toys"`.
55
+ # @param [Toys::Loader] loader A loader that can provide subcommands.
56
+ #
57
+ # @return [Toys::Utils::Usage]
58
+ #
40
59
  def initialize(tool, binary_name, loader)
41
60
  @tool = tool
42
61
  @binary_name = binary_name
43
62
  @loader = loader
44
63
  end
45
64
 
65
+ ##
66
+ # Generate a usage string.
67
+ #
68
+ # @param [Boolean] recursive If true, and the tool is a group tool,
69
+ # display all subcommands recursively. Defaults to false.
70
+ # @return [String] A usage string.
71
+ #
46
72
  def string(recursive: false)
47
73
  optparse = ::OptionParser.new
48
74
  optparse.banner = @tool.includes_executor? ? tool_banner : group_banner
@@ -61,37 +87,51 @@ module Toys
61
87
 
62
88
  private
63
89
 
90
+ #
91
+ # Returns the banner string for a normal tool
92
+ #
64
93
  def tool_banner
65
94
  banner = ["Usage:", @binary_name] + @tool.full_name
66
- banner << "[<options...>]" unless @tool.switches.empty?
67
- @tool.required_args.each do |arg_info|
95
+ banner << "[<options...>]" unless @tool.switch_definitions.empty?
96
+ @tool.required_arg_definitions.each do |arg_info|
68
97
  banner << "<#{arg_info.canonical_name}>"
69
98
  end
70
- @tool.optional_args.each do |arg_info|
99
+ @tool.optional_arg_definitions.each do |arg_info|
71
100
  banner << "[<#{arg_info.canonical_name}>]"
72
101
  end
73
- if @tool.remaining_args
74
- banner << "[<#{@tool.remaining_args.canonical_name}...>]"
102
+ if @tool.remaining_args_definition
103
+ banner << "[<#{@tool.remaining_args_definition.canonical_name}...>]"
75
104
  end
76
105
  banner.join(" ")
77
106
  end
78
107
 
108
+ #
109
+ # Returns the banner string for a group
110
+ #
79
111
  def group_banner
80
112
  (["Usage:", @binary_name] + @tool.full_name + ["<command>", "[<options...>]"]).join(" ")
81
113
  end
82
114
 
115
+ #
116
+ # Add switches from the tool to the given optionparser. Causes the
117
+ # optparser to generate documentation for those switches.
118
+ #
83
119
  def add_switches(optparse)
84
- return if @tool.switches.empty?
120
+ return if @tool.switch_definitions.empty?
85
121
  optparse.separator("")
86
122
  optparse.separator("Options:")
87
- @tool.switches.each do |switch|
123
+ @tool.switch_definitions.each do |switch|
88
124
  optparse.on(*switch.optparse_info)
89
125
  end
90
126
  end
91
127
 
128
+ #
129
+ # Add documentation for the tool's positional arguments, to the given
130
+ # option parser.
131
+ #
92
132
  def add_positional_arguments(optparse)
93
- args_to_display = @tool.required_args + @tool.optional_args
94
- args_to_display << @tool.remaining_args if @tool.remaining_args
133
+ args_to_display = @tool.required_arg_definitions + @tool.optional_arg_definitions
134
+ args_to_display << @tool.remaining_args_definition if @tool.remaining_args_definition
95
135
  return if args_to_display.empty?
96
136
  optparse.separator("")
97
137
  optparse.separator("Positional arguments:")
@@ -103,9 +143,13 @@ module Toys
103
143
  end
104
144
  end
105
145
 
146
+ #
147
+ # Add documentation for the tool's subcommands, to the given option
148
+ # parser.
149
+ #
106
150
  def add_command_list(optparse, recursive)
107
151
  name_len = @tool.full_name.length
108
- subtools = @loader.list_subtools(@tool.full_name, recursive)
152
+ subtools = @loader.list_subtools(@tool.full_name, recursive: recursive)
109
153
  return if subtools.empty?
110
154
  optparse.separator("")
111
155
  optparse.separator("Commands:")
@@ -32,5 +32,5 @@ module Toys
32
32
  # Current version of Toys
33
33
  # @return [String]
34
34
  #
35
- VERSION = "0.3.0".freeze
35
+ VERSION = "0.3.1".freeze
36
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toys
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-01 00:00:00.000000000 Z
11
+ date: 2018-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -88,15 +88,16 @@ executables:
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
+ - ".yardopts"
91
92
  - CHANGELOG.md
92
93
  - LICENSE.md
93
94
  - README.md
94
95
  - bin/toys
95
96
  - lib/toys.rb
96
- - lib/toys/builder.rb
97
97
  - lib/toys/builtins/do.rb
98
98
  - lib/toys/builtins/system.rb
99
99
  - lib/toys/cli.rb
100
+ - lib/toys/config_dsl.rb
100
101
  - lib/toys/context.rb
101
102
  - lib/toys/errors.rb
102
103
  - lib/toys/helpers.rb
@@ -105,9 +106,9 @@ files:
105
106
  - lib/toys/loader.rb
106
107
  - lib/toys/middleware.rb
107
108
  - lib/toys/middleware/base.rb
108
- - lib/toys/middleware/group_default.rb
109
109
  - lib/toys/middleware/set_verbosity.rb
110
- - lib/toys/middleware/show_tool_help.rb
110
+ - lib/toys/middleware/show_group_usage.rb
111
+ - lib/toys/middleware/show_tool_usage.rb
111
112
  - lib/toys/middleware/show_usage_errors.rb
112
113
  - lib/toys/template.rb
113
114
  - lib/toys/templates.rb
@@ -1,227 +0,0 @@
1
- # Copyright 2018 Daniel Azuma
2
- #
3
- # All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- #
8
- # * Redistributions of source code must retain the above copyright notice,
9
- # this list of conditions and the following disclaimer.
10
- # * Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- # * Neither the name of the copyright holder, nor the names of any other
14
- # contributors to this software, may be used to endorse or promote products
15
- # derived from this software without specific prior written permission.
16
- #
17
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
- # POSSIBILITY OF SUCH DAMAGE.
28
- ;
29
-
30
- module Toys
31
- ##
32
- # The object context in effect in a toys configuration file
33
- #
34
- class Builder
35
- def initialize(path, tool, remaining_words, priority, loader, type)
36
- @path = path
37
- @tool = tool
38
- @remaining_words = remaining_words
39
- @priority = priority
40
- @loader = loader
41
- @type = type
42
- end
43
-
44
- def tool(word, alias_of: nil, &block)
45
- word = word.to_s
46
- subtool = @loader.get_tool(@tool.full_name + [word], @priority, assume_parent: true)
47
- return self if subtool.nil?
48
- if alias_of
49
- if block
50
- raise ToolDefinitionError, "Cannot take a block with alias_of"
51
- end
52
- subtool.make_alias_of_word(alias_of.to_s)
53
- return self
54
- end
55
- next_remaining = Loader.next_remaining_words(@remaining_words, word)
56
- Builder.build(@path, subtool, next_remaining, @priority, @loader, block, :tool)
57
- self
58
- end
59
- alias name tool
60
-
61
- def append(word, &block)
62
- word = word.to_s
63
- subtool = @loader.get_tool(@tool.full_name + [word], nil, assume_parent: true)
64
- next_remaining = Loader.next_remaining_words(@remaining_words, word)
65
- Builder.build(@path, subtool, next_remaining, @priority, @loader, block, :append)
66
- self
67
- end
68
-
69
- def group(word, &block)
70
- word = word.to_s
71
- subtool = @loader.get_tool(@tool.full_name + [word], @priority, assume_parent: true)
72
- return self if subtool.nil?
73
- next_remaining = Loader.next_remaining_words(@remaining_words, word)
74
- Builder.build(@path, subtool, next_remaining, @priority, @loader, block, :group)
75
- self
76
- end
77
-
78
- def alias_as(word)
79
- if @tool.root?
80
- raise ToolDefinitionError, "Cannot make an alias of the root tool"
81
- end
82
- if @type == :group || @type == :append
83
- raise ToolDefinitionError, "Cannot make an alias of a group"
84
- end
85
- alias_name = @tool.full_name.slice(0..-2) + [word.to_s]
86
- alias_tool = @loader.get_tool(alias_name, @priority)
87
- alias_tool.make_alias_of(@tool.simple_name) if alias_tool
88
- self
89
- end
90
-
91
- def alias_of(word)
92
- if @tool.root?
93
- raise ToolDefinitionError, "Cannot make the root tool an alias"
94
- end
95
- if @type == :group || @type == :append
96
- raise ToolDefinitionError, "Cannot make a group an alias"
97
- end
98
- @tool.make_alias_of(word.to_s)
99
- self
100
- end
101
-
102
- def include(path)
103
- @tool.yield_definition do
104
- @loader.include_path(path, @tool.full_name, @remaining_words, @priority)
105
- end
106
- self
107
- end
108
-
109
- def expand(template_class, *args)
110
- unless template_class.is_a?(::Class)
111
- name = template_class.to_s
112
- template_class = Templates.lookup(name)
113
- if template_class.nil?
114
- raise ToolDefinitionError, "Template not found: #{name.inspect}"
115
- end
116
- end
117
- template = template_class.new(*args)
118
- yield template if block_given?
119
- instance_exec(template, &template_class.expander)
120
- self
121
- end
122
-
123
- def long_desc(desc)
124
- if @type == :append
125
- raise ToolDefinitionError, "Cannot set the description when appending"
126
- end
127
- @tool.long_desc = desc
128
- self
129
- end
130
-
131
- def desc(desc)
132
- if @type == :append
133
- raise ToolDefinitionError, "Cannot set the description when appending"
134
- end
135
- @tool.desc = desc
136
- self
137
- end
138
- alias short_desc desc
139
-
140
- def switch(key, *switches,
141
- accept: nil, default: nil, doc: nil, only_unique: false, handler: nil)
142
- if @type == :append
143
- raise ToolDefinitionError, "Cannot add a switch when appending"
144
- end
145
- @tool.add_switch(key, *switches,
146
- accept: accept, default: default, doc: doc,
147
- only_unique: only_unique, handler: handler)
148
- self
149
- end
150
-
151
- def required_arg(key, accept: nil, doc: nil)
152
- if @type == :append
153
- raise ToolDefinitionError, "Cannot add an argument when appending"
154
- end
155
- @tool.add_required_arg(key, accept: accept, doc: doc)
156
- self
157
- end
158
-
159
- def optional_arg(key, accept: nil, default: nil, doc: nil)
160
- if @type == :append
161
- raise ToolDefinitionError, "Cannot add an argument when appending"
162
- end
163
- @tool.add_optional_arg(key, accept: accept, default: default, doc: doc)
164
- self
165
- end
166
-
167
- def remaining_args(key, accept: nil, default: [], doc: nil)
168
- if @type == :append
169
- raise ToolDefinitionError, "Cannot add an argument when appending"
170
- end
171
- @tool.set_remaining_args(key, accept: accept, default: default, doc: doc)
172
- self
173
- end
174
-
175
- def execute(&block)
176
- if @type == :group || @type == :append
177
- raise ToolDefinitionError, "Cannot set the executor of a group"
178
- end
179
- @tool.executor = block
180
- self
181
- end
182
-
183
- def helper(name, &block)
184
- if @type == :group || @type == :append
185
- raise ToolDefinitionError, "Cannot define a helper method to a group"
186
- end
187
- @tool.add_helper(name, &block)
188
- self
189
- end
190
-
191
- def use(mod)
192
- if @type == :group || @type == :append
193
- raise ToolDefinitionError, "Cannot use a helper module in a group"
194
- end
195
- @tool.use_module(mod)
196
- self
197
- end
198
-
199
- def _binding
200
- binding
201
- end
202
-
203
- def self.build(path, tool, remaining_words, priority, loader, source, type)
204
- builder = new(path, tool, remaining_words, priority, loader, type)
205
- if type == :append
206
- eval_source(builder, path, source)
207
- else
208
- tool.defining_from(path) do
209
- eval_source(builder, path, source)
210
- tool.finish_definition
211
- end
212
- end
213
- tool
214
- end
215
-
216
- def self.eval_source(builder, path, source)
217
- case source
218
- when String
219
- # rubocop:disable Security/Eval
220
- eval(source, builder._binding, path, 1)
221
- # rubocop:enable Security/Eval
222
- when ::Proc
223
- builder.instance_eval(&source)
224
- end
225
- end
226
- end
227
- end