thor-exclude_pattern 0.18.0

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