toys-core 0.3.2

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.
@@ -0,0 +1,101 @@
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
+ module Utils
32
+ ##
33
+ # A helper module that provides methods to do module lookups. This is
34
+ # used to obtain named helpers, middleware, and templates from the
35
+ # respective modules.
36
+ #
37
+ # You generally do not need to use these module methods directly. Instead
38
+ # use the convenience methods {Toys::Helpers.lookup},
39
+ # {Toys::Middleware.lookup}, or {Toys::Templates.lookup}.
40
+ #
41
+ module ModuleLookup
42
+ class << self
43
+ ##
44
+ # Convert the given string to a path element. Specifically, converts
45
+ # to `lower_snake_case`.
46
+ #
47
+ # @param [String,Symbol] str String to convert.
48
+ # @return [String] Converted string
49
+ #
50
+ def to_path_name(str)
51
+ str.to_s.gsub(/([a-zA-Z])([A-Z])/) { |_m| "#{$1}_#{$2.downcase}" }.downcase
52
+ end
53
+
54
+ ##
55
+ # Convert the given string to a module name. Specifically, converts
56
+ # to `UpperCamelCase`.
57
+ #
58
+ # @param [String,Symbol] str String to convert.
59
+ # @return [String] Converted string
60
+ #
61
+ def to_module_name(str)
62
+ str.to_s.gsub(/(^|_)([a-zA-Z0-9])/) { |_m| $2.upcase }
63
+ end
64
+
65
+ ##
66
+ # Obtain a named module from the given collection. Raises an exception
67
+ # on failure.
68
+ #
69
+ # @param [String,Symbol] collection The collection to search. Typical
70
+ # values are `:helpers`, `:middleware`, and `:templates`.
71
+ # @param [String,Symbol] name The name of the module to return.
72
+ #
73
+ # @return [Module] The specified module
74
+ # @raise [LoadError] No Ruby file containing the given module could
75
+ # be found.
76
+ # @raise [NameError] The given module was not defined.
77
+ #
78
+ def lookup!(collection, name)
79
+ require "toys/#{to_path_name(collection)}/#{to_path_name(name)}"
80
+ ::Toys.const_get(to_module_name(collection)).const_get(to_module_name(name))
81
+ end
82
+
83
+ ##
84
+ # Obtain a named module from the given collection. Returns `nil` on
85
+ # failure.
86
+ #
87
+ # @param [String,Symbol] collection The collection to search. Typical
88
+ # values are `:helpers`, `:middleware`, and `:templates`.
89
+ # @param [String,Symbol] name The name of the module to return.
90
+ #
91
+ # @return [Module,nil] The specified module, or `nil` if not found.
92
+ #
93
+ def lookup(collection, name)
94
+ lookup!(collection, name)
95
+ rescue ::NameError, ::LoadError
96
+ nil
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,196 @@
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
+ module Utils
32
+ ##
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.
38
+ #
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
+ #
46
+ def self.from_context(context)
47
+ new(context[Context::TOOL], context[Context::BINARY_NAME], context[Context::LOADER])
48
+ end
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
+ #
59
+ def initialize(tool, binary_name, loader)
60
+ @tool = tool
61
+ @binary_name = binary_name
62
+ @loader = loader
63
+ end
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
+ # @param [String,nil] search An optional string to search for when
71
+ # listing subcommands. Defaults to `nil` which finds all subcommands.
72
+ # @param [Boolean] show_path If true, shows the path to the config file
73
+ # containing the tool definition (if set). Defaults to false.
74
+ #
75
+ # @return [String] A usage string.
76
+ #
77
+ def string(recursive: false, search: nil, show_path: false)
78
+ optparse = ::OptionParser.new
79
+ optparse.banner = @tool.includes_executor? ? tool_banner : group_banner
80
+ unless @tool.effective_long_desc.empty?
81
+ optparse.separator("")
82
+ optparse.separator(@tool.effective_long_desc)
83
+ end
84
+ if show_path && @tool.definition_path
85
+ optparse.separator("")
86
+ optparse.separator("Defined in #{@tool.definition_path}")
87
+ end
88
+ add_switches(optparse)
89
+ if @tool.includes_executor?
90
+ add_positional_arguments(optparse)
91
+ else
92
+ add_command_list(optparse, recursive, search)
93
+ end
94
+ optparse.to_s
95
+ end
96
+
97
+ private
98
+
99
+ #
100
+ # Returns the banner string for a normal tool
101
+ #
102
+ def tool_banner
103
+ banner = ["Usage:", @binary_name] + @tool.full_name
104
+ banner << "[<options...>]" unless @tool.switch_definitions.empty?
105
+ @tool.required_arg_definitions.each do |arg_info|
106
+ banner << "<#{arg_info.canonical_name}>"
107
+ end
108
+ @tool.optional_arg_definitions.each do |arg_info|
109
+ banner << "[<#{arg_info.canonical_name}>]"
110
+ end
111
+ if @tool.remaining_args_definition
112
+ banner << "[<#{@tool.remaining_args_definition.canonical_name}...>]"
113
+ end
114
+ banner.join(" ")
115
+ end
116
+
117
+ #
118
+ # Returns the banner string for a group
119
+ #
120
+ def group_banner
121
+ list = ["Usage:", @binary_name] +
122
+ @tool.full_name +
123
+ ["<command>", "<command-arguments...>"]
124
+ list.join(" ")
125
+ end
126
+
127
+ #
128
+ # Add switches from the tool to the given optionparser. Causes the
129
+ # optparser to generate documentation for those switches.
130
+ #
131
+ def add_switches(optparse)
132
+ return if @tool.switch_definitions.empty?
133
+ optparse.separator("")
134
+ optparse.separator("Options:")
135
+ @tool.switch_definitions.each do |switch|
136
+ optparse.on(*switch.optparse_info)
137
+ end
138
+ end
139
+
140
+ #
141
+ # Add documentation for the tool's positional arguments, to the given
142
+ # option parser.
143
+ #
144
+ def add_positional_arguments(optparse)
145
+ args_to_display = @tool.required_arg_definitions + @tool.optional_arg_definitions
146
+ args_to_display << @tool.remaining_args_definition if @tool.remaining_args_definition
147
+ return if args_to_display.empty?
148
+ optparse.separator("")
149
+ optparse.separator("Positional arguments:")
150
+ args_to_display.each do |arg_info|
151
+ optparse.separator(" #{arg_info.canonical_name.ljust(31)} #{arg_info.doc.first}")
152
+ (arg_info.doc[1..-1] || []).each do |d|
153
+ optparse.separator(" #{d}")
154
+ end
155
+ end
156
+ end
157
+
158
+ #
159
+ # Add documentation for the tool's subcommands, to the given option
160
+ # parser.
161
+ #
162
+ def add_command_list(optparse, recursive, search)
163
+ name_len = @tool.full_name.length
164
+ subtools = find_commands(recursive, search)
165
+ return if subtools.empty?
166
+ optparse.separator("")
167
+ if search
168
+ optparse.separator("Commands with search term #{search.inspect}:")
169
+ else
170
+ optparse.separator("Commands:")
171
+ end
172
+ subtools.each do |subtool|
173
+ tool_name = subtool.full_name.slice(name_len..-1).join(" ").ljust(31)
174
+ if subtool.is_a?(Alias)
175
+ optparse.separator(" #{tool_name} (Alias of #{subtool.display_target})")
176
+ else
177
+ optparse.separator(" #{tool_name} #{subtool.effective_desc}")
178
+ end
179
+ end
180
+ end
181
+
182
+ #
183
+ # Find subcommands of the current tool
184
+ #
185
+ def find_commands(recursive, search)
186
+ subtools = @loader.list_subtools(@tool.full_name, recursive: recursive)
187
+ return subtools if search.nil? || search.empty?
188
+ regex = Regexp.new("(^|\\s)#{Regexp.escape(search)}(\\s|$)", Regexp::IGNORECASE)
189
+ subtools.find_all do |tool|
190
+ regex =~ tool.display_name || regex =~ tool.effective_desc ||
191
+ regex =~ tool.effective_long_desc
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: toys-core
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Azuma
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest-focus
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest-rg
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.55.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.55.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.9'
83
+ description: A simple command line tool framework
84
+ email:
85
+ - dazuma@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".yardopts"
91
+ - CHANGELOG.md
92
+ - LICENSE.md
93
+ - README.md
94
+ - lib/toys-core.rb
95
+ - lib/toys/alias.rb
96
+ - lib/toys/cli.rb
97
+ - lib/toys/config_dsl.rb
98
+ - lib/toys/context.rb
99
+ - lib/toys/core_version.rb
100
+ - lib/toys/errors.rb
101
+ - lib/toys/helpers.rb
102
+ - lib/toys/helpers/exec.rb
103
+ - lib/toys/helpers/file_utils.rb
104
+ - lib/toys/loader.rb
105
+ - lib/toys/middleware.rb
106
+ - lib/toys/middleware/add_verbosity_switches.rb
107
+ - lib/toys/middleware/base.rb
108
+ - lib/toys/middleware/handle_usage_errors.rb
109
+ - lib/toys/middleware/set_default_descriptions.rb
110
+ - lib/toys/middleware/show_usage.rb
111
+ - lib/toys/middleware/show_version.rb
112
+ - lib/toys/template.rb
113
+ - lib/toys/templates.rb
114
+ - lib/toys/templates/clean.rb
115
+ - lib/toys/templates/gem_build.rb
116
+ - lib/toys/templates/minitest.rb
117
+ - lib/toys/templates/rubocop.rb
118
+ - lib/toys/templates/yardoc.rb
119
+ - lib/toys/tool.rb
120
+ - lib/toys/utils/module_lookup.rb
121
+ - lib/toys/utils/usage.rb
122
+ homepage: https://github.com/dazuma/toys
123
+ licenses:
124
+ - BSD-3-Clause
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 2.2.0
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.7.6
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Command line tool framework
146
+ test_files: []