atli 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +8 -0
  3. data/CHANGELOG.md +193 -0
  4. data/CONTRIBUTING.md +20 -0
  5. data/LICENSE.md +24 -0
  6. data/README.md +44 -0
  7. data/atli.gemspec +30 -0
  8. data/bin/thor +6 -0
  9. data/lib/thor.rb +868 -0
  10. data/lib/thor/actions.rb +322 -0
  11. data/lib/thor/actions/create_file.rb +104 -0
  12. data/lib/thor/actions/create_link.rb +60 -0
  13. data/lib/thor/actions/directory.rb +118 -0
  14. data/lib/thor/actions/empty_directory.rb +143 -0
  15. data/lib/thor/actions/file_manipulation.rb +364 -0
  16. data/lib/thor/actions/inject_into_file.rb +109 -0
  17. data/lib/thor/base.rb +773 -0
  18. data/lib/thor/command.rb +192 -0
  19. data/lib/thor/core_ext/hash_with_indifferent_access.rb +97 -0
  20. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  21. data/lib/thor/core_ext/ordered_hash.rb +129 -0
  22. data/lib/thor/error.rb +32 -0
  23. data/lib/thor/group.rb +281 -0
  24. data/lib/thor/invocation.rb +182 -0
  25. data/lib/thor/line_editor.rb +17 -0
  26. data/lib/thor/line_editor/basic.rb +37 -0
  27. data/lib/thor/line_editor/readline.rb +88 -0
  28. data/lib/thor/parser.rb +5 -0
  29. data/lib/thor/parser/argument.rb +70 -0
  30. data/lib/thor/parser/arguments.rb +175 -0
  31. data/lib/thor/parser/option.rb +146 -0
  32. data/lib/thor/parser/options.rb +221 -0
  33. data/lib/thor/parser/shared_option.rb +23 -0
  34. data/lib/thor/rake_compat.rb +71 -0
  35. data/lib/thor/runner.rb +324 -0
  36. data/lib/thor/shell.rb +81 -0
  37. data/lib/thor/shell/basic.rb +439 -0
  38. data/lib/thor/shell/color.rb +149 -0
  39. data/lib/thor/shell/html.rb +126 -0
  40. data/lib/thor/util.rb +268 -0
  41. data/lib/thor/version.rb +22 -0
  42. metadata +114 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5861afa66188c1398592fbe2c11f918ae6c65362
4
+ data.tar.gz: 82d992373be7e149b6ac38f28d026fdf14fe7902
5
+ SHA512:
6
+ metadata.gz: b12a02bdc6bed10d119cb8386c2d1c113fee8fd4b8cf10e16fecb0f272c5ab0e90de694524e5ad0c49042ca198f353454e069968b7001dec2645bbf68b9b3e9e
7
+ data.tar.gz: 7d84ddc3300f2c6a2c29837724f4e56512f13d77f293cf613521c1253361d3c5a66f907c078286c988568414943eba45a52510c710b9b6d59da8b63d4595bc51
data/.yardopts ADDED
@@ -0,0 +1,8 @@
1
+ --protected
2
+ --markup rdoc
3
+ lib/*.rb
4
+ lib/**/*.rb
5
+ -
6
+ LICENSE.md
7
+ README.md
8
+ doc/files/**/*.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,193 @@
1
+ ## 0.20.0
2
+ * Add `check_default_type!` to check if the default value of an option matches the defined type.
3
+ It removes the warning on usage and gives the command authors the possibility to check for programming errors.
4
+
5
+ * Add `disable_required_check!` to disable check for required options in some commands.
6
+ It is a substitute of `disable_class_options` that was not working as intended.
7
+
8
+ * Add `inject_into_module`.
9
+
10
+ ## 0.19.4, release 2016-11-28
11
+ * Rename `Thor::Base#thor_reserved_word?` to `#is_thor_reserved_word?`
12
+
13
+ ## 0.19.3, release 2016-11-27
14
+ * Output a warning instead of raising an exception when a default option value doesn't match its specified type
15
+
16
+ ## 0.19.2, release 2016-11-26
17
+ * Fix bug with handling of colors passed to `ask` (and methods like `yes?` and `no?` which it underpins)
18
+ * Allow numeric arguments to be negative
19
+ * Ensure that default option values are of the specified type (e.g. you can't specify `"foo"` as the default for a numeric option), but make symbols and strings interchangeable
20
+ * Add `Thor::Shell::Basic#indent` method for intending output
21
+ * Fix `remove_command` for an inherited command (see #451)
22
+ * Allow hash arguments to only have each key provided once (see #455)
23
+ * Allow commands to disable class options, for instance for "help" commands (see #363)
24
+ * Do not generate a negative option (`--no-no-foo`) for already negative boolean options (`--no-foo`)
25
+ * Improve compatibility of `Thor::CoreExt::HashWithIndifferentAccess` with Ruby standard library `Hash`
26
+ * Allow specifying a custom binding for template evaluation (e.g. `#key?` and `#fetch`)
27
+ * Fix support for subcommand-specific "help"s
28
+ * Use a string buffer when handling ERB for Ruby 2.3 compatibility
29
+ * Update dependencies
30
+
31
+ ## 0.19.1, release 2014-03-24
32
+ * Fix `say` non-String break regression
33
+
34
+ ## 0.19.0, release 2014-03-22
35
+ * Add support for a default to #ask
36
+ * Avoid @namespace not initialized warning
37
+ * Avoid private attribute? warning
38
+ * Fix initializing with unknown options
39
+ * Loosen required_rubygems_version for compatibility with Ubuntu 10.04
40
+ * Shell#ask: support a noecho option for stdin
41
+ * Shell#ask: change API to be :echo => false
42
+ * Display a message without a stack trace for ambiguous commands
43
+ * Make say and say_status thread safe
44
+ * Dependency for console io version check
45
+ * Alias --help to help on subcommands
46
+ * Use mime-types 1.x for Ruby 1.8.7 compatibility for Ruby 1.8 only
47
+ * Accept .tt files as templates
48
+ * Check if numeric value is in enum
49
+ * Use Readline for user input
50
+ * Fix dispatching of subcommands (concerning :help and *args)
51
+ * Fix warnings when running specs with `$VERBOSE = true`
52
+ * Make subcommand help more consistent
53
+ * Make the current command chain accessible in command
54
+
55
+ ## 0.18.1, release 2013-03-30
56
+ * Revert regressions found in 0.18.0
57
+
58
+ ## 0.18.0, release 2013-03-26
59
+ * Remove rake2thor
60
+ * Only display colors if output medium supports colors
61
+ * Pass parent_options to subcommands
62
+ * Fix non-dash-prefixed aliases
63
+ * Make error messages more helpful
64
+ * Rename "task" to "command"
65
+ * Add the method to allow for custom package name
66
+
67
+ ## 0.17.0, release 2013-01-24
68
+ * Add better support for tasks that accept arbitrary additional arguments (e.g. things like `bundle exec`)
69
+ * Add #stop_on_unknown_option!
70
+ * Only strip from stdin.gets if it wasn't ended with EOF
71
+ * Allow "send" as a task name
72
+ * Allow passing options as arguments after "--"
73
+ * Autoload Thor::Group
74
+
75
+ ## 0.16.0, release 2012-08-14
76
+ * Add enum to string arguments
77
+
78
+ ## 0.15.4, release 2012-06-29
79
+ * Fix regression when destination root contains reserved regexp characters
80
+
81
+ ## 0.15.3, release 2012-06-18
82
+ * Support strict_args_position! for backwards compatibility
83
+ * Escape Dir glob characters in paths
84
+
85
+ ## 0.15.2, released 2012-05-07
86
+ * Added print_in_columns
87
+ * Exposed terminal_width as a public API
88
+
89
+ ## 0.15.1, release 2012-05-06
90
+ * Fix Ruby 1.8 truncation bug with unicode chars
91
+ * Fix shell delegate methods to pass their block
92
+ * Don't output trailing spaces when printing the last column in a table
93
+
94
+ ## 0.15, released 2012-04-29
95
+ * Alias method_options to options
96
+ * Refactor say to allow multiple colors
97
+ * Exposed error as a public API
98
+ * Exposed file_collision as a public API
99
+ * Exposed print_wrapped as a public API
100
+ * Exposed set_color as a public API
101
+ * Fix number-formatting bugs in print_table
102
+ * Fix "indent" typo in print_table
103
+ * Fix Errno::EPIPE when piping tasks to `head`
104
+ * More friendly error messages
105
+
106
+ ## 0.14, released 2010-07-25
107
+ * Added CreateLink class and #link_file method
108
+ * Made Thor::Actions#run use system as default method for system calls
109
+ * Allow use of private methods from superclass as tasks
110
+ * Added mute(&block) method which allows to run block without any output
111
+ * Removed config[:pretend]
112
+ * Enabled underscores for command line switches
113
+ * Added Thor::Base.basename which is used by both Thor.banner and Thor::Group.banner
114
+ * Deprecated invoke() without arguments
115
+ * Added :only and :except to check_unknown_options
116
+
117
+ ## 0.13, released 2010-02-03
118
+ * Added :lazy_default which is only triggered if a switch is given
119
+ * Added Thor::Shell::HTML
120
+ * Added subcommands
121
+ * Decoupled Thor::Group and Thor, so it's easier to vendor
122
+ * Added check_unknown_options! in case you want error messages to be raised in valid switches
123
+ * run(command) should return the results of command
124
+
125
+ ## 0.12, released 2010-01-02
126
+ * Methods generated by attr_* are automatically not marked as tasks
127
+ * inject_into_file does not add the same content twice, unless :force is set
128
+ * Removed rr in favor to rspec mock framework
129
+ * Improved output for thor -T
130
+ * [#7] Do not force white color on status
131
+ * [#8] Yield a block with the filename on directory
132
+
133
+ ## 0.11, released 2009-07-01
134
+ * Added a rake compatibility layer. It allows you to use spec and rdoc tasks on
135
+ Thor classes.
136
+ * BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore
137
+ since it may cause wrong behavior in the invocation system.
138
+ * thor help now show information about any class/task. All those calls are
139
+ possible:
140
+
141
+ thor help describe
142
+ thor help describe:amazing
143
+ Or even with default namespaces:
144
+
145
+ thor help :spec
146
+ * Thor::Runner now invokes the default task if none is supplied:
147
+
148
+ thor describe # invokes the default task, usually help
149
+ * Thor::Runner now works with mappings:
150
+
151
+ thor describe -h
152
+ * Added some documentation and code refactoring.
153
+
154
+ ## 0.9.8, released 2008-10-20
155
+ * Fixed some tiny issues that were introduced lately.
156
+
157
+ ## 0.9.7, released 2008-10-13
158
+ * Setting global method options on the initialize method works as expected:
159
+ All other tasks will accept these global options in addition to their own.
160
+ * Added 'group' notion to Thor task sets (class Thor); by default all tasks
161
+ are in the 'standard' group. Running 'thor -T' will only show the standard
162
+ tasks - adding --all will show all tasks. You can also filter on a specific
163
+ group using the --group option: thor -T --group advanced
164
+
165
+ ## 0.9.6, released 2008-09-13
166
+ * Generic improvements
167
+
168
+ ## 0.9.5, released 2008-08-27
169
+ * Improve Windows compatibility
170
+ * Update (incorrect) README and task.thor sample file
171
+ * Options hash is now frozen (once returned)
172
+ * Allow magic predicates on options object. For instance: `options.force?`
173
+ * Add support for :numeric type
174
+ * BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f])
175
+ * Allow specifying optional args with default values: method_options(:user => "mislav")
176
+ * Don't write options for nil or false values. This allows, for example, turning color off when running specs.
177
+ * Exit with the status of the spec command to help CI stuff out some.
178
+
179
+ ## 0.9.4, released 2008-08-13
180
+ * Try to add Windows compatibility.
181
+ * BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore
182
+ * Allow options at the beginning of the argument list as well as the end.
183
+ * Make options available with symbol keys in addition to string keys.
184
+ * Allow true to be passed to Thor#method_options to denote a boolean option.
185
+ * If loading a thor file fails, don't give up, just print a warning and keep going.
186
+ * Make sure that we re-raise errors if they happened further down the pipe than we care about.
187
+ * Only delete the old file on updating when the installation of the new one is a success
188
+ * Make it Ruby 1.8.5 compatible.
189
+ * Don't raise an error if a boolean switch is defined multiple times.
190
+ * Thor::Options now doesn't parse through things that look like options but aren't.
191
+ * Add URI detection to install task, and make sure we don't append ".thor" to URIs
192
+ * Add rake2thor to the gem binfiles.
193
+ * Make sure local Thorfiles override system-wide ones.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,20 @@
1
+ Pull Requests
2
+ -------------
3
+ Here are some reasons why a pull request may not be merged:
4
+
5
+ 1. It hasn’t been reviewed.
6
+ 2. It doesn’t include specs for new functionality.
7
+ 3. It doesn’t include documentation for new functionality.
8
+ 4. It changes behavior without changing the relevant documentation, comments, or specs.
9
+ 5. It changes behavior of an existing public API, breaking backward compatibility.
10
+ 6. It breaks the tests on a supported platform.
11
+ 7. It doesn’t merge cleanly (requiring Git rebasing and conflict resolution).
12
+ 8. I'm busy.
13
+ 9. I don't like you.
14
+ 10. Too many parens.
15
+ 11. I'm not busy.
16
+ 12. Your code is bad, and you should feel bad.
17
+
18
+ If you would like to help in this process, you can start by evaluating open pull requests against the criteria above. For example, if a pull request does not include specs for new functionality, you can add a comment like: “If you would like this feature to be added to Thor, please add specs to ensure that it does not break in the future.” This will help move a pull request closer to being merged.
19
+
20
+ Include this emoji in the top of your ticket to signal to us that you read this file: 🐱
data/LICENSE.md ADDED
@@ -0,0 +1,24 @@
1
+ Atli is copyright (c) 2018 Neil Souza
2
+
3
+ Atli is based on Thor, and released under the same license as follows.
4
+
5
+ Thor is copyright (c) 2008 Yehuda Katz, Eric Hodel, et al.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ Atli
2
+ ====
3
+
4
+ Description
5
+ -----------
6
+ [Thor][] is a simple and efficient tool for building self-documenting command line utilities.
7
+
8
+ [Thor]: https://github.com/erikhuda/thor
9
+
10
+ Atli is a fork of Thor that adds some stuff. For the time being, it intends to be a drop-in replacement, though this might change at some point in the future.
11
+
12
+ This means that Alti still uses the Thor name and file-space. You require it like
13
+
14
+ ```ruby
15
+ require 'thor'
16
+ ```
17
+
18
+ and use it exactly like you would Thor.
19
+
20
+ As you may suspect, this is likely to wreck havoc if you have real Thor installed as well. Please choose one.
21
+
22
+ Installation
23
+ ------------
24
+ gem install atli
25
+
26
+ Usage and documentation
27
+ -----------------------
28
+
29
+ At some point, I'd like to document the additional features that Atli introduces on top of Thor, and even perhaps add general documentation with greater coverage and detail than what is available for Thor (sparse documentation is probably it's weakest point when getting started), but for now all you've got is the [Thor wiki][] and [Thor homepage][].
30
+
31
+ [Thor wiki]: https://github.com/nrser/atli/wiki
32
+ [Thor homepage]: http://whatisthor.com/
33
+
34
+ Contributing
35
+ ------------
36
+ If you would like to help, please read the [CONTRIBUTING][] file for suggestions.
37
+
38
+ [contributing]: CONTRIBUTING.md
39
+
40
+ License
41
+ -------
42
+ Released under the MIT License. See the [LICENSE][] file for further details.
43
+
44
+ [license]: LICENSE.md
data/atli.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib/", __FILE__)
3
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
4
+ require "thor/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.add_development_dependency "bundler", "~> 1.0"
8
+ spec.authors = ["Neil Souza (Atli)", "Yehuda Katz (Thor)", "José Valim (Thor)"]
9
+ spec.description = "Atli is a fork of Thor that's better or worse."
10
+ spec.email = "neil@atli.nrser.com"
11
+ spec.executables = %w(thor)
12
+ spec.files = %w(.yardopts atli.gemspec) + Dir["*.md", "bin/*", "lib/**/*.rb"]
13
+ spec.homepage = "https://github.com/nrser/atli"
14
+ spec.licenses = %w(MIT)
15
+ spec.name = "atli"
16
+ spec.require_paths = %w(lib)
17
+ spec.required_ruby_version = ">= 1.8.7"
18
+ spec.required_rubygems_version = ">= 1.3.5"
19
+ spec.summary = spec.description
20
+ spec.version = Thor::VERSION
21
+
22
+ # Dependencies
23
+ # ============================================================================
24
+ #
25
+ # NOTE Development dependencies are in `//Gemfile`
26
+ #
27
+
28
+ # My guns
29
+ spec.add_dependency "nrser", '>= 0.2.0.pre.1'
30
+ end
data/bin/thor ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require "thor/runner"
5
+ $thor_runner = true
6
+ Thor::Runner.start
data/lib/thor.rb ADDED
@@ -0,0 +1,868 @@
1
+ require "set"
2
+ require 'nrser'
3
+ require 'semantic_logger'
4
+ require "thor/base"
5
+
6
+ using NRSER
7
+
8
+ class Thor
9
+ # Atli - add {.logger} and {#logger} methods
10
+ include SemanticLogger::Loggable
11
+
12
+ class << self
13
+ # Allows for custom "Command" package naming.
14
+ #
15
+ # === Parameters
16
+ # name<String>
17
+ # options<Hash>
18
+ #
19
+ def package_name(name, _ = {})
20
+ @package_name = name.nil? || name == "" ? nil : name
21
+ end
22
+
23
+ # Sets the default command when thor is executed without an explicit
24
+ # command to be called.
25
+ #
26
+ # ==== Parameters
27
+ # meth<Symbol>:: name of the default command
28
+ #
29
+ def default_command(meth = nil)
30
+ if meth
31
+ @default_command = meth == :none ? "help" : meth.to_s
32
+ else
33
+ @default_command ||= from_superclass(:default_command, "help")
34
+ end
35
+ end
36
+ alias_method :default_task, :default_command
37
+
38
+ # Registers another Thor subclass as a command.
39
+ #
40
+ # ==== Parameters
41
+ # klass<Class>:: Thor subclass to register
42
+ # command<String>:: Subcommand name to use
43
+ # usage<String>:: Short usage for the subcommand
44
+ # description<String>:: Description for the subcommand
45
+ def register(klass, subcommand_name, usage, description, options = {})
46
+ if klass <= Thor::Group
47
+ desc usage, description, options
48
+ define_method(subcommand_name) { |*args| invoke(klass, args) }
49
+ else
50
+ desc usage, description, options
51
+ subcommand subcommand_name, klass
52
+ end
53
+ end
54
+
55
+ # Defines the usage and the description of the next command.
56
+ #
57
+ # ==== Parameters
58
+ # usage<String>
59
+ # description<String>
60
+ # options<String>
61
+ #
62
+ def desc(usage, description, options = {})
63
+ if options[:for]
64
+ command = find_and_refresh_command(options[:for])
65
+ command.usage = usage if usage
66
+ command.description = description if description
67
+ else
68
+ @usage = usage
69
+ @desc = description
70
+ @hide = options[:hide] || false
71
+ end
72
+ end
73
+
74
+ # Defines the long description of the next command.
75
+ #
76
+ # ==== Parameters
77
+ # long description<String>
78
+ #
79
+ def long_desc(long_description, options = {})
80
+ if options[:for]
81
+ command = find_and_refresh_command(options[:for])
82
+ command.long_description = long_description if long_description
83
+ else
84
+ @long_desc = long_description
85
+ end
86
+ end
87
+
88
+ # Maps an input to a command. If you define:
89
+ #
90
+ # map "-T" => "list"
91
+ #
92
+ # Running:
93
+ #
94
+ # thor -T
95
+ #
96
+ # Will invoke the list command.
97
+ #
98
+ # ==== Parameters
99
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the
100
+ # array to the given command.
101
+ #
102
+ def map(mappings = nil)
103
+ @map ||= from_superclass(:map, {})
104
+
105
+ if mappings
106
+ mappings.each do |key, value|
107
+ if key.respond_to?(:each)
108
+ key.each { |subkey| @map[subkey] = value }
109
+ else
110
+ @map[key] = value
111
+ end
112
+ end
113
+ end
114
+
115
+ @map
116
+ end
117
+
118
+ # Declares the options for the next command to be declared.
119
+ #
120
+ # ==== Parameters
121
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
122
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
123
+ # or :required (string). If you give a value, the type of the value is used.
124
+ #
125
+ def method_options(options = nil)
126
+ @method_options ||= {}
127
+ build_options(options, @method_options) if options
128
+ @method_options
129
+ end
130
+
131
+ alias_method :options, :method_options
132
+
133
+ # Adds an option to the set of method options. If :for is given as option,
134
+ # it allows you to change the options from a previous defined command.
135
+ #
136
+ # def previous_command
137
+ # # magic
138
+ # end
139
+ #
140
+ # method_option :foo => :bar, :for => :previous_command
141
+ #
142
+ # def next_command
143
+ # # magic
144
+ # end
145
+ #
146
+ # ==== Parameters
147
+ # name<Symbol>:: The name of the argument.
148
+ # options<Hash>:: Described below.
149
+ #
150
+ # ==== Options
151
+ # :desc - Description for the argument.
152
+ # :required - If the argument is required or not.
153
+ # :default - Default value for this argument. It cannot be required and
154
+ # have default values.
155
+ # :aliases - Aliases for this option.
156
+ # :type - The type of the argument, can be :string, :hash, :array,
157
+ # :numeric or :boolean.
158
+ # :banner - String to show on usage notes.
159
+ # :hide - If you want to hide this option from the help.
160
+ #
161
+ def method_option(name, options = {})
162
+ scope = if options[:for]
163
+ find_and_refresh_command(options[:for]).options
164
+ else
165
+ method_options
166
+ end
167
+
168
+ build_option(name, options, scope)
169
+ end
170
+ alias_method :option, :method_option
171
+
172
+ # Prints help information for the given command.
173
+ #
174
+ # @param [Thor::Shell] shell
175
+ #
176
+ # @param [String] command_name
177
+ #
178
+ # @param [Boolean] subcommand
179
+ # *Alti* *addition* - passed from {#help} when that command is being
180
+ # invoked as a subcommand.
181
+ #
182
+ # The values is passed through to {.banner} and eventually
183
+ # {Command#formatted_usage} so that it can properly display the usage
184
+ # message
185
+ #
186
+ # basename subcmd cmd ARGS...
187
+ #
188
+ # versus what it did when I found it:
189
+ #
190
+ # basename cmd ARGS...
191
+ #
192
+ # which, of course, doesn't work if +cmd+ is inside +subcmd+.
193
+ #
194
+ # @return [void]
195
+ # *Atli* - as far as I can tell, this method just writes to STDOUT
196
+ # and the return value is not / should not be used for anything.
197
+ #
198
+ def command_help(shell, command_name, subcommand = false)
199
+ meth = normalize_command_name(command_name)
200
+ command = all_commands[meth]
201
+ handle_no_command_error(meth) unless command
202
+
203
+ shell.say "Usage:"
204
+ shell.say " #{banner(command, nil, subcommand)}"
205
+ shell.say
206
+ class_options_help(shell, nil => command.options.values)
207
+ if command.long_description
208
+ shell.say "Description:"
209
+ shell.print_wrapped(command.long_description, :indent => 2)
210
+ else
211
+ shell.say command.description
212
+ end
213
+ end
214
+ alias_method :task_help, :command_help
215
+
216
+ # Prints help information for this class.
217
+ #
218
+ # ==== Parameters
219
+ # shell<Thor::Shell>
220
+ #
221
+ def help(shell, subcommand = false)
222
+ list = printable_commands(true, subcommand)
223
+ Thor::Util.thor_classes_in(self).each do |klass|
224
+ list += klass.printable_commands(false)
225
+ end
226
+ list.sort! { |a, b| a[0] <=> b[0] }
227
+
228
+ if defined?(@package_name) && @package_name
229
+ shell.say "#{@package_name} commands:"
230
+ else
231
+ shell.say "Commands:"
232
+ end
233
+
234
+ shell.print_table(list, :indent => 2, :truncate => true)
235
+ shell.say
236
+ class_options_help(shell)
237
+ end
238
+
239
+ # Returns commands ready to be printed.
240
+ def printable_commands(all = true, subcommand = false)
241
+ (all ? all_commands : commands).map do |_, command|
242
+ next if command.hidden?
243
+ item = []
244
+ item << banner(command, false, subcommand)
245
+ item << ( command.description ?
246
+ "# #{command.description.gsub(/\s+/m, ' ')}" : "" )
247
+ item
248
+ end.compact
249
+ end
250
+ alias_method :printable_tasks, :printable_commands
251
+
252
+ def subcommands
253
+ @subcommands ||= from_superclass(:subcommands, [])
254
+ end
255
+ alias_method :subtasks, :subcommands
256
+
257
+ def subcommand_classes
258
+ @subcommand_classes ||= {}
259
+ end
260
+
261
+ def subcommand(subcommand, subcommand_class)
262
+ subcommands << subcommand.to_s
263
+ subcommand_class.subcommand_help subcommand
264
+ subcommand_classes[subcommand.to_s] = subcommand_class
265
+
266
+ define_method(subcommand) do |*args|
267
+ args, opts = Thor::Arguments.split(args)
268
+ invoke_args = [
269
+ args,
270
+ opts,
271
+ {:invoked_via_subcommand => true, :class_options => options}
272
+ ]
273
+ invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
274
+ invoke subcommand_class, *invoke_args
275
+ end
276
+ subcommand_class.commands.each do |_meth, command|
277
+ command.ancestor_name = subcommand
278
+ end
279
+ end
280
+ alias_method :subtask, :subcommand
281
+
282
+ # Extend check unknown options to accept a hash of conditions.
283
+ #
284
+ # === Parameters
285
+ # options<Hash>: A hash containing :only and/or :except keys
286
+ def check_unknown_options!(options = {})
287
+ @check_unknown_options ||= {}
288
+ options.each do |key, value|
289
+ if value
290
+ @check_unknown_options[key] = Array(value)
291
+ else
292
+ @check_unknown_options.delete(key)
293
+ end
294
+ end
295
+ @check_unknown_options
296
+ end
297
+
298
+ # Overwrite check_unknown_options? to take subcommands and options into
299
+ # account.
300
+ def check_unknown_options?(config) #:nodoc:
301
+ options = check_unknown_options
302
+ return false unless options
303
+
304
+ command = config[:current_command]
305
+ return true unless command
306
+
307
+ name = command.name
308
+
309
+ if subcommands.include?(name)
310
+ false
311
+ elsif options[:except]
312
+ !options[:except].include?(name.to_sym)
313
+ elsif options[:only]
314
+ options[:only].include?(name.to_sym)
315
+ else
316
+ true
317
+ end
318
+ end
319
+
320
+ # Stop parsing of options as soon as an unknown option or a regular
321
+ # argument is encountered. All remaining arguments are passed to the command.
322
+ # This is useful if you have a command that can receive arbitrary additional
323
+ # options, and where those additional options should not be handled by
324
+ # Thor.
325
+ #
326
+ # ==== Example
327
+ #
328
+ # To better understand how this is useful, let's consider a command that calls
329
+ # an external command. A user may want to pass arbitrary options and
330
+ # arguments to that command. The command itself also accepts some options,
331
+ # which should be handled by Thor.
332
+ #
333
+ # class_option "verbose", :type => :boolean
334
+ # stop_on_unknown_option! :exec
335
+ # check_unknown_options! :except => :exec
336
+ #
337
+ # desc "exec", "Run a shell command"
338
+ # def exec(*args)
339
+ # puts "diagnostic output" if options[:verbose]
340
+ # Kernel.exec(*args)
341
+ # end
342
+ #
343
+ # Here +exec+ can be called with +--verbose+ to get diagnostic output,
344
+ # e.g.:
345
+ #
346
+ # $ thor exec --verbose echo foo
347
+ # diagnostic output
348
+ # foo
349
+ #
350
+ # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
351
+ #
352
+ # $ thor exec echo --verbose foo
353
+ # --verbose foo
354
+ #
355
+ # ==== Parameters
356
+ # Symbol ...:: A list of commands that should be affected.
357
+ def stop_on_unknown_option!(*command_names)
358
+ stop_on_unknown_option.merge(command_names)
359
+ end
360
+
361
+ def stop_on_unknown_option?(command) #:nodoc:
362
+ command && stop_on_unknown_option.include?(command.name.to_sym)
363
+ end
364
+
365
+ # Disable the check for required options for the given commands.
366
+ # This is useful if you have a command that does not need the required options
367
+ # to work, like help.
368
+ #
369
+ # ==== Parameters
370
+ # Symbol ...:: A list of commands that should be affected.
371
+ def disable_required_check!(*command_names)
372
+ disable_required_check.merge(command_names)
373
+ end
374
+
375
+ def disable_required_check?(command) #:nodoc:
376
+ command && disable_required_check.include?(command.name.to_sym)
377
+ end
378
+
379
+ # Atli Public Class Methods
380
+ # ========================================================================
381
+
382
+ # @return [Hash<Symbol, Thor::SharedOption]
383
+ # Get all shared options
384
+ #
385
+ def shared_method_options(options = nil)
386
+ @shared_method_options ||= begin
387
+ # Reach up the inheritance chain, if there's anyone there
388
+ if superclass.respond_to? __method__
389
+ superclass.send( __method__ ).dup
390
+ else
391
+ # Or just default to empty
392
+ {}
393
+ end
394
+ end
395
+
396
+ if options
397
+ # We don't support this (yet at least)
398
+ raise NotImplementedError,
399
+ "Bulk set not supported, use .shared_method_option"
400
+ # build_shared_options(options, @shared_method_options)
401
+ end
402
+ @shared_method_options
403
+ end
404
+ alias_method :shared_options, :shared_method_options
405
+
406
+
407
+ # Find shared options given names and groups.
408
+ #
409
+ # @param [*<Symbol>] *names
410
+ # Individual shared option names to include.
411
+ #
412
+ # @param [nil | Symbol | Enumerable<Symbol>] groups:
413
+ # Single or list of shared option groups to include.
414
+ #
415
+ # @return [Hash<Symbol, Thor::SharedOption>]
416
+ # Hash mapping option names (as {Symbol}) to instances.
417
+ #
418
+ def find_shared_method_options *names, groups: nil
419
+ groups_set = Set[*groups]
420
+
421
+ shared_method_options.
422
+ select { |name, option|
423
+ names.include?( name ) || !(option.groups & groups_set).empty?
424
+ }
425
+ end
426
+ alias_method :find_shared_options, :find_shared_method_options
427
+
428
+
429
+ # Declare a shared method option with an optional groups that can then
430
+ # be added by name or group to commands.
431
+ #
432
+ # The shared options can then be added to methods individually by name and
433
+ # collectively as groups with {Thor.include_method_options}.
434
+ #
435
+ # @example
436
+ # class MyCLI < Thor
437
+ #
438
+ # # Declare a shared option:
439
+ # shared_option :force,
440
+ # groups: :write,
441
+ # desc: "Force the operation",
442
+ # type: :boolean
443
+ #
444
+ # # ...
445
+ #
446
+ # desc "write [OPTIONS] path",
447
+ # "Write to a path"
448
+ #
449
+ # # Add the shared options to the method:
450
+ # include_options groups: :write
451
+ #
452
+ # def write path
453
+ #
454
+ # # Get a slice of `#options` with any of the `:write` group options
455
+ # # that were provided and use it in a method call:
456
+ # MyModule.write path, **option_kwds( groups: :write )
457
+ #
458
+ # end
459
+ # end
460
+ #
461
+ # @param [Symbol] name
462
+ # The name of the option.
463
+ #
464
+ # @param [**<Symbol, V>] options
465
+ # Keyword args used to initialize the {Thor::SharedOption}.
466
+ #
467
+ # All +**options+ are optional.
468
+ #
469
+ # @option options [Symbol | Array<Symbol>] :groups
470
+ # One or more _shared_ _option_ _group_ that the new option will belong
471
+ # to.
472
+ #
473
+ # Examples:
474
+ # groups: :read
475
+ # groups: [:read, :write]
476
+ #
477
+ # *NOTE* The keyword is +groups+ with an +s+! {Thor::Option} already has
478
+ # a +group+ string attribute that, as far as I can tell, is only
479
+ #
480
+ #
481
+ #
482
+ # @option options [String] :desc
483
+ # Description for the option for help and feedback.
484
+ #
485
+ # @option options [Boolean] :required
486
+ # If the option is required or not.
487
+ #
488
+ # @option options [Object] :default
489
+ # Default value for this argument.
490
+ #
491
+ # It cannot be +required+ and have default values.
492
+ #
493
+ # @option options [String | Array<String>] :aliases
494
+ # Aliases for this option.
495
+ #
496
+ # Examples:
497
+ # aliases: '-s'
498
+ # aliases: '--other-name'
499
+ # aliases: ['-s', '--other-name']
500
+ #
501
+ # @option options [:string | :hash | :array | :numeric | :boolean] :type
502
+ # Type of acceptable values, see
503
+ # {types for method options}[https://github.com/erikhuda/thor/wiki/Method-Options#types-for-method_options]
504
+ # in the Thor wiki.
505
+ #
506
+ # @option options [String] :banner
507
+ # String to show on usage notes.
508
+ #
509
+ # @option options [Boolean] :hide
510
+ # If you want to hide this option from the help.
511
+ #
512
+ # @return (see .build_shared_option)
513
+ #
514
+ def shared_method_option name, **options
515
+ # Don't think the `:for` option makes sense... that would just be a
516
+ # regular method option, right? I guess `:for` could be an array and
517
+ # apply the option to each command, but it seems like that would just
518
+ # be better as an extension to the {.method_option} behavior.
519
+ #
520
+ # So, we raise if we see it
521
+ if options.key? :for
522
+ raise ArgumentError,
523
+ ".shared_method_option does not accept the `:for` option"
524
+ end
525
+
526
+ build_shared_option(name, options)
527
+ end # #shared_method_option
528
+ alias_method :shared_option, :shared_method_option
529
+
530
+
531
+ # Add the {Thor::SharedOption} instances with +names+ and in +groups+ to
532
+ # the next defined command method.
533
+ #
534
+ # @param (see .find_shared_method_options)
535
+ # @return (see .find_shared_method_options)
536
+ #
537
+ def include_method_options *names, groups: nil
538
+ find_shared_method_options( *names, groups: groups ).
539
+ each do |name, option|
540
+ method_options[name] = option
541
+ end
542
+ end
543
+
544
+ alias_method :include_options, :include_method_options
545
+
546
+ # END Atli Public Class Methods ******************************************
547
+
548
+
549
+ protected
550
+
551
+ def stop_on_unknown_option #:nodoc:
552
+ @stop_on_unknown_option ||= Set.new
553
+ end
554
+
555
+ # help command has the required check disabled by default.
556
+ def disable_required_check #:nodoc:
557
+ @disable_required_check ||= Set.new([:help])
558
+ end
559
+
560
+ # The method responsible for dispatching given the args.
561
+ def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
562
+ meth ||= retrieve_command_name(given_args)
563
+ command = all_commands[normalize_command_name(meth)]
564
+
565
+ if !command && config[:invoked_via_subcommand]
566
+ # We're a subcommand and our first argument didn't match any of our
567
+ # commands. So we put it back and call our default command.
568
+ given_args.unshift(meth)
569
+ command = all_commands[normalize_command_name(default_command)]
570
+ end
571
+
572
+ if command
573
+ args, opts = Thor::Options.split(given_args)
574
+ if stop_on_unknown_option?(command) && !args.empty?
575
+ # given_args starts with a non-option, so we treat everything as
576
+ # ordinary arguments
577
+ args.concat opts
578
+ opts.clear
579
+ end
580
+ else
581
+ args = given_args
582
+ opts = nil
583
+ command = dynamic_command_class.new(meth)
584
+ end
585
+
586
+ opts = given_opts || opts || []
587
+ config[:current_command] = command
588
+ config[:command_options] = command.options
589
+
590
+ instance = new(args, opts, config)
591
+ yield instance if block_given?
592
+ args = instance.args
593
+ trailing = args[Range.new(arguments.size, -1)]
594
+ instance.invoke_command(command, trailing || [])
595
+ end
596
+
597
+
598
+ # The banner for this class. You can customize it if you are invoking the
599
+ # thor class by another ways which is not the Thor::Runner. It receives
600
+ # the command that is going to be invoked and a boolean which indicates if
601
+ # the namespace should be displayed as arguments.
602
+ #
603
+ # @param [Thor::Command] command
604
+ # The command to render the banner for.
605
+ #
606
+ # @param [nil | ?] namespace
607
+ # *Atli*: this argument is _not_ _used_ _at_ _all_. I don't know what it
608
+ # could or should be, but it doesn't seem like it matters at all :/
609
+ #
610
+ # @param [Boolean] subcommand
611
+ # Should be +true+ if the command was invoked as a sub-command; passed
612
+ # on to {Command#formatted_usage} so it can render correctly.
613
+ #
614
+ # @return [String]
615
+ # The banner for the command.
616
+ #
617
+ def banner(command, namespace = nil, subcommand = false)
618
+ "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
619
+ end
620
+
621
+
622
+ def baseclass #:nodoc:
623
+ Thor
624
+ end
625
+
626
+ def dynamic_command_class #:nodoc:
627
+ Thor::DynamicCommand
628
+ end
629
+
630
+ def create_command(meth) #:nodoc:
631
+ @usage ||= nil
632
+ @desc ||= nil
633
+ @long_desc ||= nil
634
+ @hide ||= nil
635
+
636
+ if @usage && @desc
637
+ base_class = @hide ? Thor::HiddenCommand : Thor::Command
638
+ commands[meth] = base_class.new(
639
+ meth,
640
+ @desc,
641
+ @long_desc,
642
+ @usage,
643
+ method_options
644
+ )
645
+ @usage, @desc, @long_desc, @method_options, @hide = nil
646
+ true
647
+ elsif all_commands[meth] || meth == "method_missing"
648
+ true
649
+ else
650
+ puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \
651
+ "Call desc if you want this method to be available as command or declare it inside a " \
652
+ "no_commands{} block. Invoked from #{caller[1].inspect}."
653
+ false
654
+ end
655
+ end
656
+ alias_method :create_task, :create_command
657
+
658
+ def initialize_added #:nodoc:
659
+ class_options.merge!(method_options)
660
+ @method_options = nil
661
+ end
662
+
663
+ # Retrieve the command name from given args.
664
+ def retrieve_command_name(args) #:nodoc:
665
+ meth = args.first.to_s unless args.empty?
666
+ args.shift if meth && (map[meth] || meth !~ /^\-/)
667
+ end
668
+ alias_method :retrieve_task_name, :retrieve_command_name
669
+
670
+ # receives a (possibly nil) command name and returns a name that is in
671
+ # the commands hash. In addition to normalizing aliases, this logic
672
+ # will determine if a shortened command is an unambiguous substring of
673
+ # a command or alias.
674
+ #
675
+ # +normalize_command_name+ also converts names like +animal-prison+
676
+ # into +animal_prison+.
677
+ def normalize_command_name(meth) #:nodoc:
678
+ return default_command.to_s.tr("-", "_") unless meth
679
+
680
+ possibilities = find_command_possibilities(meth)
681
+
682
+ if possibilities.size > 1
683
+ raise AmbiguousTaskError,
684
+ "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
685
+ end
686
+
687
+ if possibilities.empty?
688
+ meth ||= default_command
689
+ elsif map[meth]
690
+ meth = map[meth]
691
+ else
692
+ meth = possibilities.first
693
+ end
694
+
695
+ meth.to_s.tr("-", "_") # treat foo-bar as foo_bar
696
+ end
697
+ alias_method :normalize_task_name, :normalize_command_name
698
+
699
+ # this is the logic that takes the command name passed in by the user
700
+ # and determines whether it is an unambiguous substrings of a command or
701
+ # alias name.
702
+ def find_command_possibilities(meth)
703
+ len = meth.to_s.length
704
+ possibilities = all_commands.merge(map).keys.select { |n|
705
+ meth == n[0, len]
706
+ }.sort
707
+ unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
708
+
709
+ if possibilities.include?(meth)
710
+ [meth]
711
+ elsif unique_possibilities.size == 1
712
+ unique_possibilities
713
+ else
714
+ possibilities
715
+ end
716
+ end
717
+ alias_method :find_task_possibilities, :find_command_possibilities
718
+
719
+ def subcommand_help(cmd)
720
+ logger.trace __method__.to_s,
721
+ cmd: cmd,
722
+ caller: caller
723
+
724
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
725
+
726
+ # Atli - This used to be {#class_eval} (maybe to support really old
727
+ # Rubies? Who knows...) but that made it really hard to find in
728
+ # stack traces, so I switched it to {#define_method}.
729
+ #
730
+ define_method :help do |*args|
731
+
732
+ # Add the `is_subcommand = true` trailing arg
733
+ case args[-1]
734
+ when true
735
+ # pass
736
+ when false
737
+ # Weird, `false` was explicitly passed... whatever, set it to `true`
738
+ args[-1] = true
739
+ else
740
+ # "Normal" case, append it
741
+ args << true
742
+ end
743
+
744
+ super *args
745
+ end
746
+
747
+ end
748
+ alias_method :subtask_help, :subcommand_help
749
+
750
+ # Atli Protected Class Methods
751
+ # ========================================================================
752
+
753
+ # Build a Thor::SharedOption and add it to Thor.shared_method_options.
754
+ #
755
+ # The Thor::SharedOption is returned.
756
+ #
757
+ # ==== Parameters
758
+ # name<Symbol>:: The name of the argument.
759
+ # options<Hash>:: Described in both class_option and method_option,
760
+ # with the additional `:groups` shared option keyword.
761
+ def build_shared_option(name, options)
762
+ shared_method_options[name] = Thor::SharedOption.new(
763
+ name,
764
+ options.merge(:check_default_type => check_default_type?)
765
+ )
766
+ end # #build_shared_option
767
+
768
+ # END protected Class Methods **********************************************
769
+
770
+ end # class << self
771
+
772
+
773
+ protected # Instance Methods
774
+ # ==========================================================================
775
+
776
+ # Get a Hash mapping option name symbols to their values ready for
777
+ # +**+ usage in a method call for the option names and shared option
778
+ # groups.
779
+ #
780
+ # @param (see .find_shared_method_options)
781
+ #
782
+ # @return [Hash<Symbol, V>]
783
+ # Map of option name to value.
784
+ #
785
+ def option_kwds *names, groups: nil
786
+ # Transform names into a set of strings
787
+ name_set = Set.new names.map( &:to_s )
788
+
789
+ # Add groups (if any)
790
+ if groups
791
+ self.class.find_shared_options( groups: groups ).each do |name, option|
792
+ name_set << name.to_s
793
+ end
794
+ end
795
+
796
+ options.slice( *name_set ).sym_keys
797
+ end
798
+
799
+ # END protected Instance Methods *******************************************
800
+ public
801
+
802
+
803
+ # After this, {.method_added} hook is installed and defined methods become
804
+ # commands
805
+ include Thor::Base
806
+
807
+
808
+ # Commands
809
+ # ============================================================================
810
+
811
+ map HELP_MAPPINGS => :help
812
+
813
+ desc "help [COMMAND]", "Describe available commands or one specific command"
814
+ def help(*args)
815
+ is_subcommand = case args[-1]
816
+ when true, false
817
+ args.pop
818
+ else
819
+ false
820
+ end
821
+
822
+ # Will be the {String} command (get help on that command)
823
+ # or `nil` (get help on this class)
824
+ command = args.shift
825
+
826
+ # Possibly empty array of string subcommands from something like
827
+ #
828
+ # myexe help sub cmd
829
+ #
830
+ # in which case it would end up being `['cmd']` and we actually are just
831
+ # passing through and want to get help on the `cmd` subcommand.
832
+ #
833
+ subcommands = args
834
+
835
+ logger.trace "#help",
836
+ args: args,
837
+ command: command,
838
+ is_subcommand: is_subcommand,
839
+ subcommands: subcommands
840
+
841
+ if command
842
+ if self.class.subcommands.include? command
843
+ if subcommands.empty?
844
+ # Get help on a subcommand *class*
845
+ self.class.subcommand_classes[command].help(shell, true)
846
+ else
847
+ # Atli addition - handle things like `myexe help sub cmd`
848
+ # Want help on something (class or command) further down the line
849
+ invoke self.class.subcommand_classes[command],
850
+ ['help', *subcommands],
851
+ {},
852
+ {:invoked_via_subcommand => true, :class_options => options}
853
+ end
854
+ else
855
+ # Want help on a *command* of this class
856
+ #
857
+ # Atli - Now that we've modified {.command_help} to accept
858
+ # `subcommand`, pass it (it seems to have already been getting
859
+ # the correct value to here).
860
+ self.class.command_help(shell, command, is_subcommand)
861
+ end
862
+ else
863
+ # We want help on *this class itself* (list available commands)
864
+ self.class.help(shell, is_subcommand)
865
+ end
866
+ end
867
+
868
+ end # class Thor