toys-core 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []