thor-dleavitt 0.18.1

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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/CHANGELOG.md +139 -0
  4. data/LICENSE.md +20 -0
  5. data/README.md +35 -0
  6. data/Thorfile +30 -0
  7. data/bin/thor +6 -0
  8. data/lib/thor.rb +473 -0
  9. data/lib/thor/actions.rb +318 -0
  10. data/lib/thor/actions/create_file.rb +105 -0
  11. data/lib/thor/actions/create_link.rb +60 -0
  12. data/lib/thor/actions/directory.rb +119 -0
  13. data/lib/thor/actions/empty_directory.rb +137 -0
  14. data/lib/thor/actions/file_manipulation.rb +317 -0
  15. data/lib/thor/actions/inject_into_file.rb +109 -0
  16. data/lib/thor/base.rb +654 -0
  17. data/lib/thor/command.rb +136 -0
  18. data/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  19. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  20. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  21. data/lib/thor/error.rb +32 -0
  22. data/lib/thor/group.rb +282 -0
  23. data/lib/thor/invocation.rb +172 -0
  24. data/lib/thor/parser.rb +4 -0
  25. data/lib/thor/parser/argument.rb +74 -0
  26. data/lib/thor/parser/arguments.rb +171 -0
  27. data/lib/thor/parser/option.rb +121 -0
  28. data/lib/thor/parser/options.rb +218 -0
  29. data/lib/thor/rake_compat.rb +72 -0
  30. data/lib/thor/runner.rb +322 -0
  31. data/lib/thor/shell.rb +88 -0
  32. data/lib/thor/shell/basic.rb +422 -0
  33. data/lib/thor/shell/color.rb +148 -0
  34. data/lib/thor/shell/html.rb +127 -0
  35. data/lib/thor/util.rb +270 -0
  36. data/lib/thor/version.rb +3 -0
  37. data/spec/actions/create_file_spec.rb +170 -0
  38. data/spec/actions/create_link_spec.rb +95 -0
  39. data/spec/actions/directory_spec.rb +169 -0
  40. data/spec/actions/empty_directory_spec.rb +129 -0
  41. data/spec/actions/file_manipulation_spec.rb +382 -0
  42. data/spec/actions/inject_into_file_spec.rb +135 -0
  43. data/spec/actions_spec.rb +331 -0
  44. data/spec/base_spec.rb +291 -0
  45. data/spec/command_spec.rb +80 -0
  46. data/spec/core_ext/hash_with_indifferent_access_spec.rb +48 -0
  47. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  48. data/spec/exit_condition_spec.rb +19 -0
  49. data/spec/fixtures/application.rb +2 -0
  50. data/spec/fixtures/app{1}/README +3 -0
  51. data/spec/fixtures/bundle/execute.rb +6 -0
  52. data/spec/fixtures/bundle/main.thor +1 -0
  53. data/spec/fixtures/command.thor +10 -0
  54. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  55. data/spec/fixtures/doc/COMMENTER +11 -0
  56. data/spec/fixtures/doc/README +3 -0
  57. data/spec/fixtures/doc/block_helper.rb +3 -0
  58. data/spec/fixtures/doc/config.rb +1 -0
  59. data/spec/fixtures/doc/config.yaml.tt +1 -0
  60. data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
  61. data/spec/fixtures/enum.thor +10 -0
  62. data/spec/fixtures/group.thor +128 -0
  63. data/spec/fixtures/invoke.thor +118 -0
  64. data/spec/fixtures/path with spaces b/data/spec/fixtures/path with → spaces +0 -0
  65. data/spec/fixtures/preserve/script.sh +3 -0
  66. data/spec/fixtures/script.thor +220 -0
  67. data/spec/fixtures/subcommand.thor +17 -0
  68. data/spec/group_spec.rb +222 -0
  69. data/spec/helper.rb +67 -0
  70. data/spec/invocation_spec.rb +108 -0
  71. data/spec/parser/argument_spec.rb +53 -0
  72. data/spec/parser/arguments_spec.rb +66 -0
  73. data/spec/parser/option_spec.rb +202 -0
  74. data/spec/parser/options_spec.rb +400 -0
  75. data/spec/rake_compat_spec.rb +72 -0
  76. data/spec/register_spec.rb +197 -0
  77. data/spec/runner_spec.rb +241 -0
  78. data/spec/shell/basic_spec.rb +330 -0
  79. data/spec/shell/color_spec.rb +95 -0
  80. data/spec/shell/html_spec.rb +31 -0
  81. data/spec/shell_spec.rb +47 -0
  82. data/spec/subcommand_spec.rb +30 -0
  83. data/spec/thor_spec.rb +499 -0
  84. data/spec/util_spec.rb +196 -0
  85. data/thor.gemspec +24 -0
  86. metadata +191 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 085616190f304695fc41dc399e2bfb8d4e628bf2
4
+ data.tar.gz: 638362ce23d4e1f3d4a4974ad4fa7ffa2075399b
5
+ SHA512:
6
+ metadata.gz: 4249e4542b9a4ac5aee73cb8808cd9ab8334bf9f9c4d00e4aad46fc1a6e636b40459a09659f861651b3629980b5dbc213750f726c745977c37efc9b7850b1346
7
+ data.tar.gz: ed6190400b5c8b22653da211b85f159524ca51898a3d959773ba0d62349a810a9930bfa6c54e9e74ad97f3b25b54d3bbcbc036c0b05e6425c2f4eb16c80582ee
@@ -0,0 +1,5 @@
1
+ lib/*.rb
2
+ lib/**/*.rb
3
+ -
4
+ CHANGELOG.rdoc
5
+ LICENSE.md
@@ -0,0 +1,139 @@
1
+ ## 0.18.1, release 2013-03-30
2
+ * Revert regressions found in 0.18.0
3
+
4
+ ## 0.18.0, release 2013-03-26
5
+ * Remove rake2thor
6
+ * Only display colors if output medium supports colors
7
+ * Pass parent_options to subcommands
8
+ * Fix non-dash-prefixed aliases
9
+ * Make error messages more helpful
10
+ * Rename "task" to "command"
11
+ * Add the method to allow for custom package name
12
+
13
+ ## 0.17.0, release 2013-01-24
14
+ * Add better support for tasks that accept arbitrary additional arguments (e.g. things like `bundle exec`)
15
+ * Add #stop_on_unknown_option!
16
+ * Only strip from stdin.gets if it wasn't ended with EOF
17
+ * Allow "send" as a task name
18
+ * Allow passing options as arguments after "--"
19
+ * Autoload Thor::Group
20
+
21
+ ## 0.16.0, release 2012-08-14
22
+ * Add enum to string arguments
23
+
24
+ ## 0.15.4, release 2012-06-29
25
+ * Fix regression when destination root contains reserved regexp characters
26
+
27
+ ## 0.15.3, release 2012-06-18
28
+ * Support strict_args_position! for backwards compatibility
29
+ * Escape Dir glob characters in paths
30
+
31
+ ## 0.15.2, released 2012-05-07
32
+ * Added print_in_columns
33
+ * Exposed terminal_width as a public API
34
+
35
+ ## 0.15.1, release 2012-05-06
36
+ * Fix Ruby 1.8 truncation bug with unicode chars
37
+ * Fix shell delegate methods to pass their block
38
+ * Don't output trailing spaces when printing the last column in a table
39
+
40
+ ## 0.15, released 2012-04-29
41
+ * Alias method_options to options
42
+ * Refactor say to allow multiple colors
43
+ * Exposed error as a public API
44
+ * Exposed file_collision as a public API
45
+ * Exposed print_wrapped as a public API
46
+ * Exposed set_color as a public API
47
+ * Fix number-formatting bugs in print_table
48
+ * Fix "indent" typo in print_table
49
+ * Fix Errno::EPIPE when piping tasks to `head`
50
+ * More friendly error messages
51
+
52
+ ## 0.14, released 2010-07-25
53
+ * Added CreateLink class and #link_file method
54
+ * Made Thor::Actions#run use system as default method for system calls
55
+ * Allow use of private methods from superclass as tasks
56
+ * Added mute(&block) method which allows to run block without any output
57
+ * Removed config[:pretend]
58
+ * Enabled underscores for command line switches
59
+ * Added Thor::Base.basename which is used by both Thor.banner and Thor::Group.banner
60
+ * Deprecated invoke() without arguments
61
+ * Added :only and :except to check_unknown_options
62
+
63
+ ## 0.13, released 2010-02-03
64
+ * Added :lazy_default which is only triggered if a switch is given
65
+ * Added Thor::Shell::HTML
66
+ * Added subcommands
67
+ * Decoupled Thor::Group and Thor, so it's easier to vendor
68
+ * Added check_unknown_options! in case you want error messages to be raised in valid switches
69
+ * run(command) should return the results of command
70
+
71
+ ## 0.12, released 2010-01-02
72
+ * Methods generated by attr_* are automatically not marked as tasks
73
+ * inject_into_file does not add the same content twice, unless :force is set
74
+ * Removed rr in favor to rspec mock framework
75
+ * Improved output for thor -T
76
+ * [#7] Do not force white color on status
77
+ * [#8] Yield a block with the filename on directory
78
+
79
+ ## 0.11, released 2009-07-01
80
+ * Added a rake compatibility layer. It allows you to use spec and rdoc tasks on
81
+ Thor classes.
82
+ * BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore
83
+ since it may cause wrong behavior in the invocation system.
84
+ * thor help now show information about any class/task. All those calls are
85
+ possible:
86
+
87
+ thor help describe
88
+ thor help describe:amazing
89
+ Or even with default namespaces:
90
+
91
+ thor help :spec
92
+ * Thor::Runner now invokes the default task if none is supplied:
93
+
94
+ thor describe # invokes the default task, usually help
95
+ * Thor::Runner now works with mappings:
96
+
97
+ thor describe -h
98
+ * Added some documentation and code refactoring.
99
+
100
+ ## 0.9.8, released 2008-10-20
101
+ * Fixed some tiny issues that were introduced lately.
102
+
103
+ ## 0.9.7, released 2008-10-13
104
+ * Setting global method options on the initialize method works as expected:
105
+ All other tasks will accept these global options in addition to their own.
106
+ * Added 'group' notion to Thor task sets (class Thor); by default all tasks
107
+ are in the 'standard' group. Running 'thor -T' will only show the standard
108
+ tasks - adding --all will show all tasks. You can also filter on a specific
109
+ group using the --group option: thor -T --group advanced
110
+
111
+ ## 0.9.6, released 2008-09-13
112
+ * Generic improvements
113
+
114
+ ## 0.9.5, released 2008-08-27
115
+ * Improve Windows compatibility
116
+ * Update (incorrect) README and task.thor sample file
117
+ * Options hash is now frozen (once returned)
118
+ * Allow magic predicates on options object. For instance: `options.force?`
119
+ * Add support for :numeric type
120
+ * BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f])
121
+ * Allow specifying optional args with default values: method_options(:user => "mislav")
122
+ * Don't write options for nil or false values. This allows, for example, turning color off when running specs.
123
+ * Exit with the status of the spec command to help CI stuff out some.
124
+
125
+ ## 0.9.4, released 2008-08-13
126
+ * Try to add Windows compatibility.
127
+ * BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore
128
+ * Allow options at the beginning of the argument list as well as the end.
129
+ * Make options available with symbol keys in addition to string keys.
130
+ * Allow true to be passed to Thor#method_options to denote a boolean option.
131
+ * If loading a thor file fails, don't give up, just print a warning and keep going.
132
+ * Make sure that we re-raise errors if they happened further down the pipe than we care about.
133
+ * Only delete the old file on updating when the installation of the new one is a success
134
+ * Make it Ruby 1.8.5 compatible.
135
+ * Don't raise an error if a boolean switch is defined multiple times.
136
+ * Thor::Options now doesn't parse through things that look like options but aren't.
137
+ * Add URI detection to install task, and make sure we don't append ".thor" to URIs
138
+ * Add rake2thor to the gem binfiles.
139
+ * Make sure local Thorfiles override system-wide ones.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Yehuda Katz, Eric Hodel, et al.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ [![Gem Version](https://badge.fury.io/rb/thor.png)](https://rubygems.org/gems/thor)
2
+ [![Build Status](https://secure.travis-ci.org/erikhuda/thor.png?branch=master)](http://travis-ci.org/erikhuda/thor)
3
+ [![Dependency Status](https://gemnasium.com/erikhuda/thor.png?travis)](https://gemnasium.com/erikhuda/thor)
4
+ [![Code Climate](https://codeclimate.com/github/erikhuda/thor.png)](https://codeclimate.com/github/erikhuda/thor)
5
+ [![Coverage Status](https://coveralls.io/repos/erikhuda/thor/badge.png?branch=master)](https://coveralls.io/r/erikhuda/thor)
6
+
7
+ Thor
8
+ ====
9
+
10
+ Description
11
+ -----------
12
+ Thor is a simple and efficient tool for building self-documenting command line
13
+ utilities. It removes the pain of parsing command line options, writing
14
+ "USAGE:" banners, and can also be used as an alternative to the [Rake][rake]
15
+ build tool. The syntax is Rake-like, so it should be familiar to most Rake
16
+ users.
17
+
18
+ [rake]: https://github.com/jimweirich/rake
19
+
20
+ Installation
21
+ ------------
22
+ gem install thor
23
+
24
+ Usage and documentation
25
+ -----------------------
26
+ Please see the [wiki][] for basic usage and other documentation on using Thor. You can also checkout the [official homepage][homepage].
27
+
28
+ [wiki]: https://github.com/erikhuda/thor/wiki
29
+ [homepage]: http://whatisthor.com/
30
+
31
+ License
32
+ -------
33
+ Released under the MIT License. See the [LICENSE][] file for further details.
34
+
35
+ [license]: LICENSE.md
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ $:.unshift File.expand_path("../lib", __FILE__)
3
+
4
+ require 'bundler'
5
+ require 'thor/rake_compat'
6
+
7
+ class Default < Thor
8
+ include Thor::RakeCompat
9
+ Bundler::GemHelper.install_tasks
10
+
11
+ desc "build", "Build thor-#{Thor::VERSION}.gem into the pkg directory"
12
+ def build
13
+ Rake::Task["build"].execute
14
+ end
15
+
16
+ desc "install", "Build and install thor-#{Thor::VERSION}.gem into system gems"
17
+ def install
18
+ Rake::Task["install"].execute
19
+ end
20
+
21
+ desc "release", "Create tag v#{Thor::VERSION} and build and push thor-#{Thor::VERSION}.gem to Rubygems"
22
+ def release
23
+ Rake::Task["release"].execute
24
+ end
25
+
26
+ desc "spec", "Run RSpec code examples"
27
+ def spec
28
+ exec "rspec spec"
29
+ end
30
+ end
@@ -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
@@ -0,0 +1,473 @@
1
+ require 'set'
2
+ require 'thor/base'
3
+
4
+ class Thor
5
+ class << self
6
+ # Allows for custom "Command" package naming.
7
+ #
8
+ # === Parameters
9
+ # name<String>
10
+ # options<Hash>
11
+ #
12
+ def package_name(name, options={})
13
+ @package_name = name.nil? || name == '' ? nil : name
14
+ end
15
+
16
+ # Sets the default command when thor is executed without an explicit command to be called.
17
+ #
18
+ # ==== Parameters
19
+ # meth<Symbol>:: name of the default command
20
+ #
21
+ def default_command(meth=nil)
22
+ @default_command = case meth
23
+ when :none
24
+ 'help'
25
+ when nil
26
+ @default_command || from_superclass(:default_command, 'help')
27
+ else
28
+ meth.to_s
29
+ end
30
+ end
31
+ alias default_task default_command
32
+
33
+ # Registers another Thor subclass as a command.
34
+ #
35
+ # ==== Parameters
36
+ # klass<Class>:: Thor subclass to register
37
+ # command<String>:: Subcommand name to use
38
+ # usage<String>:: Short usage for the subcommand
39
+ # description<String>:: Description for the subcommand
40
+ def register(klass, subcommand_name, usage, description, options={})
41
+ if klass <= Thor::Group
42
+ desc usage, description, options
43
+ define_method(subcommand_name) { |*args| invoke(klass, args) }
44
+ else
45
+ desc usage, description, options
46
+ subcommand subcommand_name, klass
47
+ end
48
+ end
49
+
50
+ # Defines the usage and the description of the next command.
51
+ #
52
+ # ==== Parameters
53
+ # usage<String>
54
+ # description<String>
55
+ # options<String>
56
+ #
57
+ def desc(usage, description, options={})
58
+ if options[:for]
59
+ command = find_and_refresh_command(options[:for])
60
+ command.usage = usage if usage
61
+ command.description = description if description
62
+ else
63
+ @usage, @desc, @hide = usage, description, options[:hide] || false
64
+ end
65
+ end
66
+
67
+ # Defines the long description of the next command.
68
+ #
69
+ # ==== Parameters
70
+ # long description<String>
71
+ #
72
+ def long_desc(long_description, options={})
73
+ if options[:for]
74
+ command = find_and_refresh_command(options[:for])
75
+ command.long_description = long_description if long_description
76
+ else
77
+ @long_desc = long_description
78
+ end
79
+ end
80
+
81
+ # Maps an input to a command. If you define:
82
+ #
83
+ # map "-T" => "list"
84
+ #
85
+ # Running:
86
+ #
87
+ # thor -T
88
+ #
89
+ # Will invoke the list command.
90
+ #
91
+ # ==== Parameters
92
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
93
+ #
94
+ def map(mappings=nil)
95
+ @map ||= from_superclass(:map, {})
96
+
97
+ if mappings
98
+ mappings.each do |key, value|
99
+ if key.respond_to?(:each)
100
+ key.each {|subkey| @map[subkey] = value}
101
+ else
102
+ @map[key] = value
103
+ end
104
+ end
105
+ end
106
+
107
+ @map
108
+ end
109
+
110
+ # Declares the options for the next command to be declared.
111
+ #
112
+ # ==== Parameters
113
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
114
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
115
+ # or :required (string). If you give a value, the type of the value is used.
116
+ #
117
+ def method_options(options=nil)
118
+ @method_options ||= {}
119
+ build_options(options, @method_options) if options
120
+ @method_options
121
+ end
122
+
123
+ alias options method_options
124
+
125
+ # Adds an option to the set of method options. If :for is given as option,
126
+ # it allows you to change the options from a previous defined command.
127
+ #
128
+ # def previous_command
129
+ # # magic
130
+ # end
131
+ #
132
+ # method_option :foo => :bar, :for => :previous_command
133
+ #
134
+ # def next_command
135
+ # # magic
136
+ # end
137
+ #
138
+ # ==== Parameters
139
+ # name<Symbol>:: The name of the argument.
140
+ # options<Hash>:: Described below.
141
+ #
142
+ # ==== Options
143
+ # :desc - Description for the argument.
144
+ # :required - If the argument is required or not.
145
+ # :default - Default value for this argument. It cannot be required and have default values.
146
+ # :aliases - Aliases for this option.
147
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
148
+ # :banner - String to show on usage notes.
149
+ # :hide - If you want to hide this option from the help.
150
+ #
151
+ def method_option(name, options={})
152
+ scope = if options[:for]
153
+ find_and_refresh_command(options[:for]).options
154
+ else
155
+ method_options
156
+ end
157
+
158
+ build_option(name, options, scope)
159
+ end
160
+ alias option method_option
161
+
162
+ # Prints help information for the given command.
163
+ #
164
+ # ==== Parameters
165
+ # shell<Thor::Shell>
166
+ # command_name<String>
167
+ #
168
+ def command_help(shell, command_name)
169
+ meth = normalize_command_name(command_name)
170
+ command = all_commands[meth]
171
+ handle_no_command_error(meth) unless command
172
+
173
+ shell.say "Usage:"
174
+ shell.say " #{banner(command)}"
175
+ shell.say
176
+ class_options_help(shell, nil => command.options.map { |_, o| o })
177
+ if command.long_description
178
+ shell.say "Description:"
179
+ shell.print_wrapped(command.long_description, :indent => 2)
180
+ else
181
+ shell.say command.description
182
+ end
183
+ end
184
+ alias task_help command_help
185
+
186
+ # Prints help information for this class.
187
+ #
188
+ # ==== Parameters
189
+ # shell<Thor::Shell>
190
+ #
191
+ def help(shell, subcommand = false)
192
+ list = printable_commands(true, subcommand)
193
+ Thor::Util.thor_classes_in(self).each do |klass|
194
+ list += klass.printable_commands(false)
195
+ end
196
+ list.sort!{ |a,b| a[0] <=> b[0] }
197
+
198
+ if @package_name
199
+ shell.say "#{@package_name} commands:"
200
+ else
201
+ shell.say "Commands:"
202
+ end
203
+
204
+ shell.print_table(list, :indent => 2, :truncate => true)
205
+ shell.say
206
+ class_options_help(shell)
207
+ end
208
+
209
+ # Returns commands ready to be printed.
210
+ def printable_commands(all = true, subcommand = false)
211
+ (all ? all_commands : commands).map do |_, command|
212
+ next if command.hidden?
213
+ item = []
214
+ item << banner(command, false, subcommand)
215
+ item << (command.description ? "# #{command.description.gsub(/\s+/m,' ')}" : "")
216
+ item
217
+ end.compact
218
+ end
219
+ alias printable_tasks printable_commands
220
+
221
+ def subcommands
222
+ @subcommands ||= from_superclass(:subcommands, [])
223
+ end
224
+ alias subtasks subcommands
225
+
226
+ def subcommand(subcommand, subcommand_class)
227
+ self.subcommands << subcommand.to_s
228
+ subcommand_class.subcommand_help subcommand
229
+
230
+ define_method(subcommand) do |*args|
231
+ args, opts = Thor::Arguments.split(args)
232
+ invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options
233
+ end
234
+ end
235
+ alias subtask subcommand
236
+
237
+ # Extend check unknown options to accept a hash of conditions.
238
+ #
239
+ # === Parameters
240
+ # options<Hash>: A hash containing :only and/or :except keys
241
+ def check_unknown_options!(options={})
242
+ @check_unknown_options ||= Hash.new
243
+ options.each do |key, value|
244
+ if value
245
+ @check_unknown_options[key] = Array(value)
246
+ else
247
+ @check_unknown_options.delete(key)
248
+ end
249
+ end
250
+ @check_unknown_options
251
+ end
252
+
253
+ # Overwrite check_unknown_options? to take subcommands and options into account.
254
+ def check_unknown_options?(config) #:nodoc:
255
+ options = check_unknown_options
256
+ return false unless options
257
+
258
+ command = config[:current_command]
259
+ return true unless command
260
+
261
+ name = command.name
262
+
263
+ if subcommands.include?(name)
264
+ false
265
+ elsif options[:except]
266
+ !options[:except].include?(name.to_sym)
267
+ elsif options[:only]
268
+ options[:only].include?(name.to_sym)
269
+ else
270
+ true
271
+ end
272
+ end
273
+
274
+ # Stop parsing of options as soon as an unknown option or a regular
275
+ # argument is encountered. All remaining arguments are passed to the command.
276
+ # This is useful if you have a command that can receive arbitrary additional
277
+ # options, and where those additional options should not be handled by
278
+ # Thor.
279
+ #
280
+ # ==== Example
281
+ #
282
+ # To better understand how this is useful, let's consider a command that calls
283
+ # an external command. A user may want to pass arbitrary options and
284
+ # arguments to that command. The command itself also accepts some options,
285
+ # which should be handled by Thor.
286
+ #
287
+ # class_option "verbose", :type => :boolean
288
+ # stop_on_unknown_option! :exec
289
+ # check_unknown_options! :except => :exec
290
+ #
291
+ # desc "exec", "Run a shell command"
292
+ # def exec(*args)
293
+ # puts "diagnostic output" if options[:verbose]
294
+ # Kernel.exec(*args)
295
+ # end
296
+ #
297
+ # Here +exec+ can be called with +--verbose+ to get diagnostic output,
298
+ # e.g.:
299
+ #
300
+ # $ thor exec --verbose echo foo
301
+ # diagnostic output
302
+ # foo
303
+ #
304
+ # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
305
+ #
306
+ # $ thor exec echo --verbose foo
307
+ # --verbose foo
308
+ #
309
+ # ==== Parameters
310
+ # Symbol ...:: A list of commands that should be affected.
311
+ def stop_on_unknown_option!(*command_names)
312
+ @stop_on_unknown_option ||= Set.new
313
+ @stop_on_unknown_option.merge(command_names)
314
+ end
315
+
316
+ def stop_on_unknown_option?(command) #:nodoc:
317
+ command && !@stop_on_unknown_option.nil? && @stop_on_unknown_option.include?(command.name.to_sym)
318
+ end
319
+
320
+ protected
321
+
322
+ # The method responsible for dispatching given the args.
323
+ def dispatch(meth, given_args, given_opts, config) #:nodoc:
324
+ # There is an edge case when dispatching from a subcommand.
325
+ # A problem occurs invoking the default command. This case occurs
326
+ # when arguments are passed and a default command is defined, and
327
+ # the first given_args does not match the default command.
328
+ # Thor use "help" by default so we skip that case.
329
+ # Note the call to retrieve_command_name. It's called with
330
+ # given_args.dup since that method calls args.shift. Then lookup
331
+ # the command normally. If the first item in given_args is not
332
+ # a command then use the default command. The given_args will be
333
+ # intact later since dup was used.
334
+ if config[:invoked_via_subcommand] && given_args.size >= 1 && default_command != "help" && given_args.first != default_command
335
+ meth ||= retrieve_command_name(given_args.dup)
336
+ command = all_commands[normalize_command_name(meth)]
337
+ command ||= all_commands[normalize_command_name(default_command)]
338
+ else
339
+ meth ||= retrieve_command_name(given_args)
340
+ command = all_commands[normalize_command_name(meth)]
341
+ end
342
+
343
+ if command
344
+ args, opts = Thor::Options.split(given_args)
345
+ if stop_on_unknown_option?(command) && !args.empty?
346
+ # given_args starts with a non-option, so we treat everything as
347
+ # ordinary arguments
348
+ args.concat opts
349
+ opts.clear
350
+ end
351
+ else
352
+ args, opts = given_args, nil
353
+ command = Thor::DynamicCommand.new(meth)
354
+ end
355
+
356
+ opts = given_opts || opts || []
357
+ config.merge!(:current_command => command, :command_options => command.options)
358
+
359
+ instance = new(args, opts, config)
360
+ yield instance if block_given?
361
+ args = instance.args
362
+ trailing = args[Range.new(arguments.size, -1)]
363
+ instance.invoke_command(command, trailing || [])
364
+ end
365
+
366
+ # The banner for this class. You can customize it if you are invoking the
367
+ # thor class by another ways which is not the Thor::Runner. It receives
368
+ # the command that is going to be invoked and a boolean which indicates if
369
+ # the namespace should be displayed as arguments.
370
+ #
371
+ def banner(command, namespace = nil, subcommand = false)
372
+ "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
373
+ end
374
+
375
+ def baseclass #:nodoc:
376
+ Thor
377
+ end
378
+
379
+ def create_command(meth) #:nodoc:
380
+ if @usage && @desc
381
+ base_class = @hide ? Thor::HiddenCommand : Thor::Command
382
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
383
+ @usage, @desc, @long_desc, @method_options, @hide = nil
384
+ true
385
+ elsif self.all_commands[meth] || meth == "method_missing"
386
+ true
387
+ else
388
+ puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " <<
389
+ "Call desc if you want this method to be available as command or declare it inside a " <<
390
+ "no_commands{} block. Invoked from #{caller[1].inspect}."
391
+ false
392
+ end
393
+ end
394
+ alias create_task create_command
395
+
396
+ def initialize_added #:nodoc:
397
+ class_options.merge!(method_options)
398
+ @method_options = nil
399
+ end
400
+
401
+ # Retrieve the command name from given args.
402
+ def retrieve_command_name(args) #:nodoc:
403
+ meth = args.first.to_s unless args.empty?
404
+ if meth && (map[meth] || meth !~ /^\-/)
405
+ args.shift
406
+ else
407
+ nil
408
+ end
409
+ end
410
+ alias retrieve_task_name retrieve_command_name
411
+
412
+ # receives a (possibly nil) command name and returns a name that is in
413
+ # the commands hash. In addition to normalizing aliases, this logic
414
+ # will determine if a shortened command is an unambiguous substring of
415
+ # a command or alias.
416
+ #
417
+ # +normalize_command_name+ also converts names like +animal-prison+
418
+ # into +animal_prison+.
419
+ def normalize_command_name(meth) #:nodoc:
420
+ return default_command.to_s.gsub('-', '_') unless meth
421
+
422
+ possibilities = find_command_possibilities(meth)
423
+ if possibilities.size > 1
424
+ raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
425
+ elsif possibilities.size < 1
426
+ meth = meth || default_command
427
+ elsif map[meth]
428
+ meth = map[meth]
429
+ else
430
+ meth = possibilities.first
431
+ end
432
+
433
+ meth.to_s.gsub('-','_') # treat foo-bar as foo_bar
434
+ end
435
+ alias normalize_task_name normalize_command_name
436
+
437
+ # this is the logic that takes the command name passed in by the user
438
+ # and determines whether it is an unambiguous substrings of a command or
439
+ # alias name.
440
+ def find_command_possibilities(meth)
441
+ len = meth.to_s.length
442
+ possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
443
+ unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
444
+
445
+ if possibilities.include?(meth)
446
+ [meth]
447
+ elsif unique_possibilities.size == 1
448
+ unique_possibilities
449
+ else
450
+ possibilities
451
+ end
452
+ end
453
+ alias find_task_possibilities find_command_possibilities
454
+
455
+ def subcommand_help(cmd)
456
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
457
+ class_eval <<-RUBY
458
+ def help(command = nil, subcommand = true); super; end
459
+ RUBY
460
+ end
461
+ alias subtask_help subcommand_help
462
+
463
+ end
464
+
465
+ include Thor::Base
466
+
467
+ map HELP_MAPPINGS => :help
468
+
469
+ desc "help [COMMAND]", "Describe available commands or one specific command"
470
+ def help(command = nil, subcommand = false)
471
+ command ? self.class.command_help(shell, command) : self.class.help(shell, subcommand)
472
+ end
473
+ end