thor 1.1.0 → 1.4.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.
- checksums.yaml +4 -4
 - data/README.md +3 -9
 - data/lib/thor/actions/create_file.rb +3 -2
 - data/lib/thor/actions/directory.rb +1 -1
 - data/lib/thor/actions/empty_directory.rb +1 -1
 - data/lib/thor/actions/file_manipulation.rb +58 -26
 - data/lib/thor/actions/inject_into_file.rb +16 -6
 - data/lib/thor/actions.rb +21 -17
 - data/lib/thor/base.rb +140 -14
 - data/lib/thor/command.rb +13 -4
 - data/lib/thor/core_ext/hash_with_indifferent_access.rb +10 -0
 - data/lib/thor/error.rb +16 -20
 - data/lib/thor/group.rb +12 -1
 - data/lib/thor/invocation.rb +1 -1
 - data/lib/thor/nested_context.rb +2 -2
 - data/lib/thor/parser/argument.rb +17 -1
 - data/lib/thor/parser/arguments.rb +33 -17
 - data/lib/thor/parser/option.rb +28 -9
 - data/lib/thor/parser/options.rb +65 -8
 - data/lib/thor/rake_compat.rb +2 -2
 - data/lib/thor/runner.rb +41 -31
 - data/lib/thor/shell/basic.rb +59 -169
 - data/lib/thor/shell/color.rb +4 -46
 - data/lib/thor/shell/column_printer.rb +29 -0
 - data/lib/thor/shell/html.rb +4 -46
 - data/lib/thor/shell/lcs_diff.rb +49 -0
 - data/lib/thor/shell/table_printer.rb +118 -0
 - data/lib/thor/shell/terminal.rb +42 -0
 - data/lib/thor/shell/wrapped_printer.rb +38 -0
 - data/lib/thor/shell.rb +2 -2
 - data/lib/thor/util.rb +9 -8
 - data/lib/thor/version.rb +1 -1
 - data/lib/thor.rb +166 -8
 - data/thor.gemspec +19 -14
 - metadata +14 -12
 - data/CHANGELOG.md +0 -228
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 50c78a27ef16cfc96930bc60d8637e86acd69c94b932cc745bb2ed5c3e1605c8
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 00b8a88f938047a55fe3ffadb6bd019666d82813ff584209a338bd8e6d3f16a7
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 86fb64f2b698f06f5c7a0bd14523338802b374ae3d42879d71f94d542598216595176ce744b9ea84196d87657ec4cdaa9f73954ad15124b89109ee87a304eadc
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 81f3b4ce1bca3d43efe2aecf00c88372b6673e3a82706e75ef018cc17badf3b3f2a267fe926b86225b112bbe1bd6e0257115327aa006984c89d8ddc434b88d56
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -2,14 +2,8 @@ Thor 
     | 
|
| 
       2 
2 
     | 
    
         
             
            ====
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            [][gem]
         
     | 
| 
       5 
     | 
    
         
            -
            [][travis]
         
     | 
| 
       6 
     | 
    
         
            -
            [][codeclimate]
         
     | 
| 
       7 
     | 
    
         
            -
            [][coveralls]
         
     | 
| 
       8 
5 
     | 
    
         | 
| 
       9 
6 
     | 
    
         
             
            [gem]: https://rubygems.org/gems/thor
         
     | 
| 
       10 
     | 
    
         
            -
            [travis]: http://travis-ci.org/erikhuda/thor
         
     | 
| 
       11 
     | 
    
         
            -
            [codeclimate]: https://codeclimate.com/github/erikhuda/thor
         
     | 
| 
       12 
     | 
    
         
            -
            [coveralls]: https://coveralls.io/r/erikhuda/thor
         
     | 
| 
       13 
7 
     | 
    
         | 
| 
       14 
8 
     | 
    
         
             
            Description
         
     | 
| 
       15 
9 
     | 
    
         
             
            -----------
         
     | 
| 
         @@ -21,7 +15,7 @@ users. 
     | 
|
| 
       21 
15 
     | 
    
         | 
| 
       22 
16 
     | 
    
         
             
            Please note: Thor, by design, is a system tool created to allow seamless file and url
         
     | 
| 
       23 
17 
     | 
    
         
             
            access, which should not receive application user input. It relies on [open-uri][open-uri],
         
     | 
| 
       24 
     | 
    
         
            -
            which combined with application user input would provide a command injection attack
         
     | 
| 
      
 18 
     | 
    
         
            +
            which, combined with application user input, would provide a command injection attack
         
     | 
| 
       25 
19 
     | 
    
         
             
            vector.
         
     | 
| 
       26 
20 
     | 
    
         | 
| 
       27 
21 
     | 
    
         
             
            [rake]: https://github.com/ruby/rake
         
     | 
| 
         @@ -33,9 +27,9 @@ Installation 
     | 
|
| 
       33 
27 
     | 
    
         | 
| 
       34 
28 
     | 
    
         
             
            Usage and documentation
         
     | 
| 
       35 
29 
     | 
    
         
             
            -----------------------
         
     | 
| 
       36 
     | 
    
         
            -
            Please see the [wiki][] for basic usage and other documentation on using Thor. You can also  
     | 
| 
      
 30 
     | 
    
         
            +
            Please see the [wiki][] for basic usage and other documentation on using Thor. You can also check out the [official homepage][homepage].
         
     | 
| 
       37 
31 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
            [wiki]: https://github.com/ 
     | 
| 
      
 32 
     | 
    
         
            +
            [wiki]: https://github.com/rails/thor/wiki
         
     | 
| 
       39 
33 
     | 
    
         
             
            [homepage]: http://whatisthor.com/
         
     | 
| 
       40 
34 
     | 
    
         | 
| 
       41 
35 
     | 
    
         
             
            Contributing
         
     | 
| 
         @@ -43,7 +43,8 @@ class Thor 
     | 
|
| 
       43 
43 
     | 
    
         
             
                  # Boolean:: true if it is identical, false otherwise.
         
     | 
| 
       44 
44 
     | 
    
         
             
                  #
         
     | 
| 
       45 
45 
     | 
    
         
             
                  def identical?
         
     | 
| 
       46 
     | 
    
         
            -
                     
     | 
| 
      
 46 
     | 
    
         
            +
                    # binread uses ASCII-8BIT, so to avoid false negatives, the string must use the same
         
     | 
| 
      
 47 
     | 
    
         
            +
                    exists? && File.binread(destination) == String.new(render).force_encoding("ASCII-8BIT")
         
     | 
| 
       47 
48 
     | 
    
         
             
                  end
         
     | 
| 
       48 
49 
     | 
    
         | 
| 
       49 
50 
     | 
    
         
             
                  # Holds the content to be added to the file.
         
     | 
| 
         @@ -60,7 +61,7 @@ class Thor 
     | 
|
| 
       60 
61 
     | 
    
         
             
                    invoke_with_conflict_check do
         
     | 
| 
       61 
62 
     | 
    
         
             
                      require "fileutils"
         
     | 
| 
       62 
63 
     | 
    
         
             
                      FileUtils.mkdir_p(File.dirname(destination))
         
     | 
| 
       63 
     | 
    
         
            -
                      File.open(destination, "wb") { |f| f.write render }
         
     | 
| 
      
 64 
     | 
    
         
            +
                      File.open(destination, "wb", config[:perm]) { |f| f.write render }
         
     | 
| 
       64 
65 
     | 
    
         
             
                    end
         
     | 
| 
       65 
66 
     | 
    
         
             
                    given_destination
         
     | 
| 
       66 
67 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -58,7 +58,7 @@ class Thor 
     | 
|
| 
       58 
58 
     | 
    
         
             
                  def initialize(base, source, destination = nil, config = {}, &block)
         
     | 
| 
       59 
59 
     | 
    
         
             
                    @source = File.expand_path(Dir[Util.escape_globs(base.find_in_source_paths(source.to_s))].first)
         
     | 
| 
       60 
60 
     | 
    
         
             
                    @block  = block
         
     | 
| 
       61 
     | 
    
         
            -
                    super(base, destination, {: 
     | 
| 
      
 61 
     | 
    
         
            +
                    super(base, destination, {recursive: true}.merge(config))
         
     | 
| 
       62 
62 
     | 
    
         
             
                  end
         
     | 
| 
       63 
63 
     | 
    
         | 
| 
       64 
64 
     | 
    
         
             
                  def invoke!
         
     | 
| 
         @@ -10,7 +10,6 @@ class Thor 
     | 
|
| 
       10 
10 
     | 
    
         
             
                # destination<String>:: the relative path to the destination root.
         
     | 
| 
       11 
11 
     | 
    
         
             
                # config<Hash>:: give :verbose => false to not log the status, and
         
     | 
| 
       12 
12 
     | 
    
         
             
                #                :mode => :preserve, to preserve the file mode from the source.
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
13 
     | 
    
         
             
                #
         
     | 
| 
       15 
14 
     | 
    
         
             
                # ==== Examples
         
     | 
| 
       16 
15 
     | 
    
         
             
                #
         
     | 
| 
         @@ -66,12 +65,15 @@ class Thor 
     | 
|
| 
       66 
65 
     | 
    
         
             
                # ==== Parameters
         
     | 
| 
       67 
66 
     | 
    
         
             
                # source<String>:: the address of the given content.
         
     | 
| 
       68 
67 
     | 
    
         
             
                # destination<String>:: the relative path to the destination root.
         
     | 
| 
       69 
     | 
    
         
            -
                # config<Hash>:: give :verbose => false to not log the status 
     | 
| 
      
 68 
     | 
    
         
            +
                # config<Hash>:: give :verbose => false to not log the status, and
         
     | 
| 
      
 69 
     | 
    
         
            +
                #                :http_headers => <Hash> to add headers to an http request.
         
     | 
| 
       70 
70 
     | 
    
         
             
                #
         
     | 
| 
       71 
71 
     | 
    
         
             
                # ==== Examples
         
     | 
| 
       72 
72 
     | 
    
         
             
                #
         
     | 
| 
       73 
73 
     | 
    
         
             
                #   get "http://gist.github.com/103208", "doc/README"
         
     | 
| 
       74 
74 
     | 
    
         
             
                #
         
     | 
| 
      
 75 
     | 
    
         
            +
                #   get "http://gist.github.com/103208", "doc/README", :http_headers => {"Content-Type" => "application/json"}
         
     | 
| 
      
 76 
     | 
    
         
            +
                #
         
     | 
| 
       75 
77 
     | 
    
         
             
                #   get "http://gist.github.com/103208" do |content|
         
     | 
| 
       76 
78 
     | 
    
         
             
                #     content.split("\n").first
         
     | 
| 
       77 
79 
     | 
    
         
             
                #   end
         
     | 
| 
         @@ -82,10 +84,10 @@ class Thor 
     | 
|
| 
       82 
84 
     | 
    
         | 
| 
       83 
85 
     | 
    
         
             
                  render = if source =~ %r{^https?\://}
         
     | 
| 
       84 
86 
     | 
    
         
             
                    require "open-uri"
         
     | 
| 
       85 
     | 
    
         
            -
                    URI.send(:open, source) { |input| input.binmode.read }
         
     | 
| 
      
 87 
     | 
    
         
            +
                    URI.send(:open, source, config.fetch(:http_headers, {})) { |input| input.binmode.read }
         
     | 
| 
       86 
88 
     | 
    
         
             
                  else
         
     | 
| 
       87 
89 
     | 
    
         
             
                    source = File.expand_path(find_in_source_paths(source.to_s))
         
     | 
| 
       88 
     | 
    
         
            -
                    open(source) { |input| input.binmode.read }
         
     | 
| 
      
 90 
     | 
    
         
            +
                    File.open(source) { |input| input.binmode.read }
         
     | 
| 
       89 
91 
     | 
    
         
             
                  end
         
     | 
| 
       90 
92 
     | 
    
         | 
| 
       91 
93 
     | 
    
         
             
                  destination ||= if block_given?
         
     | 
| 
         @@ -120,12 +122,7 @@ class Thor 
     | 
|
| 
       120 
122 
     | 
    
         
             
                  context = config.delete(:context) || instance_eval("binding")
         
     | 
| 
       121 
123 
     | 
    
         | 
| 
       122 
124 
     | 
    
         
             
                  create_file destination, nil, config do
         
     | 
| 
       123 
     | 
    
         
            -
                     
     | 
| 
       124 
     | 
    
         
            -
                    capturable_erb = if match && match[1] >= "2.2.0" # Ruby 2.6+
         
     | 
| 
       125 
     | 
    
         
            -
                      CapturableERB.new(::File.binread(source), :trim_mode => "-", :eoutvar => "@output_buffer")
         
     | 
| 
       126 
     | 
    
         
            -
                    else
         
     | 
| 
       127 
     | 
    
         
            -
                      CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer")
         
     | 
| 
       128 
     | 
    
         
            -
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
                    capturable_erb = CapturableERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer")
         
     | 
| 
       129 
126 
     | 
    
         
             
                    content = capturable_erb.tap do |erb|
         
     | 
| 
       130 
127 
     | 
    
         
             
                      erb.filename = source
         
     | 
| 
       131 
128 
     | 
    
         
             
                    end.result(context)
         
     | 
| 
         @@ -210,9 +207,9 @@ class Thor 
     | 
|
| 
       210 
207 
     | 
    
         
             
                #
         
     | 
| 
       211 
208 
     | 
    
         
             
                # ==== Examples
         
     | 
| 
       212 
209 
     | 
    
         
             
                #
         
     | 
| 
       213 
     | 
    
         
            -
                #   inject_into_class "app/controllers/application_controller.rb", ApplicationController, "  filter_parameter :password\n"
         
     | 
| 
      
 210 
     | 
    
         
            +
                #   inject_into_class "app/controllers/application_controller.rb", "ApplicationController", "  filter_parameter :password\n"
         
     | 
| 
       214 
211 
     | 
    
         
             
                #
         
     | 
| 
       215 
     | 
    
         
            -
                #   inject_into_class "app/controllers/application_controller.rb", ApplicationController do
         
     | 
| 
      
 212 
     | 
    
         
            +
                #   inject_into_class "app/controllers/application_controller.rb", "ApplicationController" do
         
     | 
| 
       216 
213 
     | 
    
         
             
                #     "  filter_parameter :password\n"
         
     | 
| 
       217 
214 
     | 
    
         
             
                #   end
         
     | 
| 
       218 
215 
     | 
    
         
             
                #
         
     | 
| 
         @@ -233,9 +230,9 @@ class Thor 
     | 
|
| 
       233 
230 
     | 
    
         
             
                #
         
     | 
| 
       234 
231 
     | 
    
         
             
                # ==== Examples
         
     | 
| 
       235 
232 
     | 
    
         
             
                #
         
     | 
| 
       236 
     | 
    
         
            -
                #   inject_into_module "app/helpers/application_helper.rb", ApplicationHelper, "  def help; 'help'; end\n"
         
     | 
| 
      
 233 
     | 
    
         
            +
                #   inject_into_module "app/helpers/application_helper.rb", "ApplicationHelper", "  def help; 'help'; end\n"
         
     | 
| 
       237 
234 
     | 
    
         
             
                #
         
     | 
| 
       238 
     | 
    
         
            -
                #   inject_into_module "app/helpers/application_helper.rb", ApplicationHelper do
         
     | 
| 
      
 235 
     | 
    
         
            +
                #   inject_into_module "app/helpers/application_helper.rb", "ApplicationHelper" do
         
     | 
| 
       239 
236 
     | 
    
         
             
                #     "  def help; 'help'; end\n"
         
     | 
| 
       240 
237 
     | 
    
         
             
                #   end
         
     | 
| 
       241 
238 
     | 
    
         
             
                #
         
     | 
| 
         @@ -245,6 +242,35 @@ class Thor 
     | 
|
| 
       245 
242 
     | 
    
         
             
                  insert_into_file(path, *(args << config), &block)
         
     | 
| 
       246 
243 
     | 
    
         
             
                end
         
     | 
| 
       247 
244 
     | 
    
         | 
| 
      
 245 
     | 
    
         
            +
                # Run a regular expression replacement on a file, raising an error if the
         
     | 
| 
      
 246 
     | 
    
         
            +
                # contents of the file are not changed.
         
     | 
| 
      
 247 
     | 
    
         
            +
                #
         
     | 
| 
      
 248 
     | 
    
         
            +
                # ==== Parameters
         
     | 
| 
      
 249 
     | 
    
         
            +
                # path<String>:: path of the file to be changed
         
     | 
| 
      
 250 
     | 
    
         
            +
                # flag<Regexp|String>:: the regexp or string to be replaced
         
     | 
| 
      
 251 
     | 
    
         
            +
                # replacement<String>:: the replacement, can be also given as a block
         
     | 
| 
      
 252 
     | 
    
         
            +
                # config<Hash>:: give :verbose => false to not log the status, and
         
     | 
| 
      
 253 
     | 
    
         
            +
                #                :force => true, to force the replacement regardless of runner behavior.
         
     | 
| 
      
 254 
     | 
    
         
            +
                #
         
     | 
| 
      
 255 
     | 
    
         
            +
                # ==== Example
         
     | 
| 
      
 256 
     | 
    
         
            +
                #
         
     | 
| 
      
 257 
     | 
    
         
            +
                #   gsub_file! 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
         
     | 
| 
      
 258 
     | 
    
         
            +
                #
         
     | 
| 
      
 259 
     | 
    
         
            +
                #   gsub_file! 'README', /rake/, :green do |match|
         
     | 
| 
      
 260 
     | 
    
         
            +
                #     match << " no more. Use thor!"
         
     | 
| 
      
 261 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 262 
     | 
    
         
            +
                #
         
     | 
| 
      
 263 
     | 
    
         
            +
                def gsub_file!(path, flag, *args, &block)
         
     | 
| 
      
 264 
     | 
    
         
            +
                  config = args.last.is_a?(Hash) ? args.pop : {}
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                  return unless behavior == :invoke || config.fetch(:force, false)
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                  path = File.expand_path(path, destination_root)
         
     | 
| 
      
 269 
     | 
    
         
            +
                  say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
                  actually_gsub_file(path, flag, args, true, &block) unless options[:pretend]
         
     | 
| 
      
 272 
     | 
    
         
            +
                end
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
       248 
274 
     | 
    
         
             
                # Run a regular expression replacement on a file.
         
     | 
| 
       249 
275 
     | 
    
         
             
                #
         
     | 
| 
       250 
276 
     | 
    
         
             
                # ==== Parameters
         
     | 
| 
         @@ -252,7 +278,7 @@ class Thor 
     | 
|
| 
       252 
278 
     | 
    
         
             
                # flag<Regexp|String>:: the regexp or string to be replaced
         
     | 
| 
       253 
279 
     | 
    
         
             
                # replacement<String>:: the replacement, can be also given as a block
         
     | 
| 
       254 
280 
     | 
    
         
             
                # config<Hash>:: give :verbose => false to not log the status, and
         
     | 
| 
       255 
     | 
    
         
            -
                #                :force => true, to force the replacement  
     | 
| 
      
 281 
     | 
    
         
            +
                #                :force => true, to force the replacement regardless of runner behavior.
         
     | 
| 
       256 
282 
     | 
    
         
             
                #
         
     | 
| 
       257 
283 
     | 
    
         
             
                # ==== Example
         
     | 
| 
       258 
284 
     | 
    
         
             
                #
         
     | 
| 
         @@ -270,16 +296,11 @@ class Thor 
     | 
|
| 
       270 
296 
     | 
    
         
             
                  path = File.expand_path(path, destination_root)
         
     | 
| 
       271 
297 
     | 
    
         
             
                  say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
         
     | 
| 
       272 
298 
     | 
    
         | 
| 
       273 
     | 
    
         
            -
                  unless options[:pretend]
         
     | 
| 
       274 
     | 
    
         
            -
                    content = File.binread(path)
         
     | 
| 
       275 
     | 
    
         
            -
                    content.gsub!(flag, *args, &block)
         
     | 
| 
       276 
     | 
    
         
            -
                    File.open(path, "wb") { |file| file.write(content) }
         
     | 
| 
       277 
     | 
    
         
            -
                  end
         
     | 
| 
      
 299 
     | 
    
         
            +
                  actually_gsub_file(path, flag, args, false, &block) unless options[:pretend]
         
     | 
| 
       278 
300 
     | 
    
         
             
                end
         
     | 
| 
       279 
301 
     | 
    
         | 
| 
       280 
     | 
    
         
            -
                # Uncomment all lines matching a given regex. 
     | 
| 
       281 
     | 
    
         
            -
                #  
     | 
| 
       282 
     | 
    
         
            -
                # between the comment hash and the beginning of the line.
         
     | 
| 
      
 302 
     | 
    
         
            +
                # Uncomment all lines matching a given regex. Preserves indentation before
         
     | 
| 
      
 303 
     | 
    
         
            +
                # the comment hash and removes the hash and any immediate following space.
         
     | 
| 
       283 
304 
     | 
    
         
             
                #
         
     | 
| 
       284 
305 
     | 
    
         
             
                # ==== Parameters
         
     | 
| 
       285 
306 
     | 
    
         
             
                # path<String>:: path of the file to be changed
         
     | 
| 
         @@ -293,7 +314,7 @@ class Thor 
     | 
|
| 
       293 
314 
     | 
    
         
             
                def uncomment_lines(path, flag, *args)
         
     | 
| 
       294 
315 
     | 
    
         
             
                  flag = flag.respond_to?(:source) ? flag.source : flag
         
     | 
| 
       295 
316 
     | 
    
         | 
| 
       296 
     | 
    
         
            -
                  gsub_file(path, /^(\s*)#[[:blank:]] 
     | 
| 
      
 317 
     | 
    
         
            +
                  gsub_file(path, /^(\s*)#[[:blank:]]?(.*#{flag})/, '\1\2', *args)
         
     | 
| 
       297 
318 
     | 
    
         
             
                end
         
     | 
| 
       298 
319 
     | 
    
         | 
| 
       299 
320 
     | 
    
         
             
                # Comment all lines matching a given regex.  It will leave the space
         
     | 
| 
         @@ -331,7 +352,7 @@ class Thor 
     | 
|
| 
       331 
352 
     | 
    
         
             
                  path = File.expand_path(path, destination_root)
         
     | 
| 
       332 
353 
     | 
    
         | 
| 
       333 
354 
     | 
    
         
             
                  say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
         
     | 
| 
       334 
     | 
    
         
            -
                  if !options[:pretend] && File.exist?(path)
         
     | 
| 
      
 355 
     | 
    
         
            +
                  if !options[:pretend] && (File.exist?(path) || File.symlink?(path))
         
     | 
| 
       335 
356 
     | 
    
         
             
                    require "fileutils"
         
     | 
| 
       336 
357 
     | 
    
         
             
                    ::FileUtils.rm_rf(path)
         
     | 
| 
       337 
358 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -352,7 +373,7 @@ class Thor 
     | 
|
| 
       352 
373 
     | 
    
         
             
                end
         
     | 
| 
       353 
374 
     | 
    
         | 
| 
       354 
375 
     | 
    
         
             
                def with_output_buffer(buf = "".dup) #:nodoc:
         
     | 
| 
       355 
     | 
    
         
            -
                  raise ArgumentError, "Buffer  
     | 
| 
      
 376 
     | 
    
         
            +
                  raise ArgumentError, "Buffer cannot be a frozen object" if buf.frozen?
         
     | 
| 
       356 
377 
     | 
    
         
             
                  old_buffer = output_buffer
         
     | 
| 
       357 
378 
     | 
    
         
             
                  self.output_buffer = buf
         
     | 
| 
       358 
379 
     | 
    
         
             
                  yield
         
     | 
| 
         @@ -361,6 +382,17 @@ class Thor 
     | 
|
| 
       361 
382 
     | 
    
         
             
                  self.output_buffer = old_buffer
         
     | 
| 
       362 
383 
     | 
    
         
             
                end
         
     | 
| 
       363 
384 
     | 
    
         | 
| 
      
 385 
     | 
    
         
            +
                def actually_gsub_file(path, flag, args, error_on_no_change, &block)
         
     | 
| 
      
 386 
     | 
    
         
            +
                  content = File.binread(path)
         
     | 
| 
      
 387 
     | 
    
         
            +
                  success = content.gsub!(flag, *args, &block)
         
     | 
| 
      
 388 
     | 
    
         
            +
             
     | 
| 
      
 389 
     | 
    
         
            +
                  if success.nil? && error_on_no_change
         
     | 
| 
      
 390 
     | 
    
         
            +
                    raise Thor::Error, "The content of #{path} did not change"
         
     | 
| 
      
 391 
     | 
    
         
            +
                  end
         
     | 
| 
      
 392 
     | 
    
         
            +
             
     | 
| 
      
 393 
     | 
    
         
            +
                  File.open(path, "wb") { |file| file.write(content) }
         
     | 
| 
      
 394 
     | 
    
         
            +
                end
         
     | 
| 
      
 395 
     | 
    
         
            +
             
     | 
| 
       364 
396 
     | 
    
         
             
                # Thor::Actions#capture depends on what kind of buffer is used in ERB.
         
     | 
| 
       365 
397 
     | 
    
         
             
                # Thus CapturableERB fixes ERB to use String buffer.
         
     | 
| 
       366 
398 
     | 
    
         
             
                class CapturableERB < ERB
         
     | 
| 
         @@ -21,7 +21,7 @@ class Thor 
     | 
|
| 
       21 
21 
     | 
    
         
             
                #     gems.split(" ").map{ |gem| "  config.gem :#{gem}" }.join("\n")
         
     | 
| 
       22 
22 
     | 
    
         
             
                #   end
         
     | 
| 
       23 
23 
     | 
    
         
             
                #
         
     | 
| 
       24 
     | 
    
         
            -
                WARNINGS = { 
     | 
| 
      
 24 
     | 
    
         
            +
                WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                def insert_into_file(destination, *args, &block)
         
     | 
| 
       27 
27 
     | 
    
         
             
                  data = block_given? ? block : args.shift
         
     | 
| 
         @@ -37,7 +37,7 @@ class Thor 
     | 
|
| 
       37 
37 
     | 
    
         
             
                  attr_reader :replacement, :flag, :behavior
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         
             
                  def initialize(base, destination, data, config)
         
     | 
| 
       40 
     | 
    
         
            -
                    super(base, destination, {: 
     | 
| 
      
 40 
     | 
    
         
            +
                    super(base, destination, {verbose: true}.merge(config))
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
42 
     | 
    
         
             
                    @behavior, @flag = if @config.key?(:after)
         
     | 
| 
       43 
43 
     | 
    
         
             
                      [:after, @config.delete(:after)]
         
     | 
| 
         @@ -59,6 +59,8 @@ class Thor 
     | 
|
| 
       59 
59 
     | 
    
         
             
                    if exists?
         
     | 
| 
       60 
60 
     | 
    
         
             
                      if replace!(/#{flag}/, content, config[:force])
         
     | 
| 
       61 
61 
     | 
    
         
             
                        say_status(:invoke)
         
     | 
| 
      
 62 
     | 
    
         
            +
                      elsif replacement_present?
         
     | 
| 
      
 63 
     | 
    
         
            +
                        say_status(:unchanged, color: :blue)
         
     | 
| 
       62 
64 
     | 
    
         
             
                      else
         
     | 
| 
       63 
65 
     | 
    
         
             
                        say_status(:unchanged, warning: WARNINGS[:unchanged_no_flag], color: :red)
         
     | 
| 
       64 
66 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -96,6 +98,8 @@ class Thor 
     | 
|
| 
       96 
98 
     | 
    
         
             
                      end
         
     | 
| 
       97 
99 
     | 
    
         
             
                    elsif warning
         
     | 
| 
       98 
100 
     | 
    
         
             
                      warning
         
     | 
| 
      
 101 
     | 
    
         
            +
                    elsif behavior == :unchanged
         
     | 
| 
      
 102 
     | 
    
         
            +
                      :unchanged
         
     | 
| 
       99 
103 
     | 
    
         
             
                    else
         
     | 
| 
       100 
104 
     | 
    
         
             
                      :subtract
         
     | 
| 
       101 
105 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -103,15 +107,21 @@ class Thor 
     | 
|
| 
       103 
107 
     | 
    
         
             
                    super(status, (color || config[:verbose]))
         
     | 
| 
       104 
108 
     | 
    
         
             
                  end
         
     | 
| 
       105 
109 
     | 
    
         | 
| 
      
 110 
     | 
    
         
            +
                  def content
         
     | 
| 
      
 111 
     | 
    
         
            +
                    @content ||= File.read(destination)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  def replacement_present?
         
     | 
| 
      
 115 
     | 
    
         
            +
                    content.include?(replacement)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
       106 
118 
     | 
    
         
             
                  # Adds the content to the file.
         
     | 
| 
       107 
119 
     | 
    
         
             
                  #
         
     | 
| 
       108 
120 
     | 
    
         
             
                  def replace!(regexp, string, force)
         
     | 
| 
       109 
     | 
    
         
            -
                     
     | 
| 
       110 
     | 
    
         
            -
                    content = File.read(destination)
         
     | 
| 
       111 
     | 
    
         
            -
                    if force || !content.include?(replacement)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    if force || !replacement_present?
         
     | 
| 
       112 
122 
     | 
    
         
             
                      success = content.gsub!(regexp, string)
         
     | 
| 
       113 
123 
     | 
    
         | 
| 
       114 
     | 
    
         
            -
                      File.open(destination, "wb") { |file| file.write(content) }
         
     | 
| 
      
 124 
     | 
    
         
            +
                      File.open(destination, "wb") { |file| file.write(content) } unless pretend?
         
     | 
| 
       115 
125 
     | 
    
         
             
                      success
         
     | 
| 
       116 
126 
     | 
    
         
             
                    end
         
     | 
| 
       117 
127 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/thor/actions.rb
    CHANGED
    
    | 
         @@ -46,17 +46,17 @@ class Thor 
     | 
|
| 
       46 
46 
     | 
    
         
             
                  # Add runtime options that help actions execution.
         
     | 
| 
       47 
47 
     | 
    
         
             
                  #
         
     | 
| 
       48 
48 
     | 
    
         
             
                  def add_runtime_options!
         
     | 
| 
       49 
     | 
    
         
            -
                    class_option :force, : 
     | 
| 
       50 
     | 
    
         
            -
                                         : 
     | 
| 
      
 49 
     | 
    
         
            +
                    class_option :force, type: :boolean, aliases: "-f", group: :runtime,
         
     | 
| 
      
 50 
     | 
    
         
            +
                                         desc: "Overwrite files that already exist"
         
     | 
| 
       51 
51 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
                    class_option :pretend, : 
     | 
| 
       53 
     | 
    
         
            -
                                           : 
     | 
| 
      
 52 
     | 
    
         
            +
                    class_option :pretend, type: :boolean, aliases: "-p", group: :runtime,
         
     | 
| 
      
 53 
     | 
    
         
            +
                                           desc: "Run but do not make any changes"
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
                    class_option :quiet, : 
     | 
| 
       56 
     | 
    
         
            -
                                         : 
     | 
| 
      
 55 
     | 
    
         
            +
                    class_option :quiet, type: :boolean, aliases: "-q", group: :runtime,
         
     | 
| 
      
 56 
     | 
    
         
            +
                                         desc: "Suppress status output"
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
                    class_option :skip, : 
     | 
| 
       59 
     | 
    
         
            -
                                        : 
     | 
| 
      
 58 
     | 
    
         
            +
                    class_option :skip, type: :boolean, aliases: "-s", group: :runtime,
         
     | 
| 
      
 59 
     | 
    
         
            +
                                        desc: "Skip files that already exist"
         
     | 
| 
       60 
60 
     | 
    
         
             
                  end
         
     | 
| 
       61 
61 
     | 
    
         
             
                end
         
     | 
| 
       62 
62 
     | 
    
         | 
| 
         @@ -113,9 +113,9 @@ class Thor 
     | 
|
| 
       113 
113 
     | 
    
         
             
                #
         
     | 
| 
       114 
114 
     | 
    
         
             
                def relative_to_original_destination_root(path, remove_dot = true)
         
     | 
| 
       115 
115 
     | 
    
         
             
                  root = @destination_stack[0]
         
     | 
| 
       116 
     | 
    
         
            -
                  if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil,  
     | 
| 
      
 116 
     | 
    
         
            +
                  if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ""].include?(path[root.size..root.size])
         
     | 
| 
       117 
117 
     | 
    
         
             
                    path = path.dup
         
     | 
| 
       118 
     | 
    
         
            -
                    path[0...root.size] =  
     | 
| 
      
 118 
     | 
    
         
            +
                    path[0...root.size] = "."
         
     | 
| 
       119 
119 
     | 
    
         
             
                    remove_dot ? (path[2..-1] || "") : path
         
     | 
| 
       120 
120 
     | 
    
         
             
                  else
         
     | 
| 
       121 
121 
     | 
    
         
             
                    path
         
     | 
| 
         @@ -161,6 +161,8 @@ class Thor 
     | 
|
| 
       161 
161 
     | 
    
         
             
                # to the block you provide. The path is set back to the previous path when
         
     | 
| 
       162 
162 
     | 
    
         
             
                # the method exits.
         
     | 
| 
       163 
163 
     | 
    
         
             
                #
         
     | 
| 
      
 164 
     | 
    
         
            +
                # Returns the value yielded by the block.
         
     | 
| 
      
 165 
     | 
    
         
            +
                #
         
     | 
| 
       164 
166 
     | 
    
         
             
                # ==== Parameters
         
     | 
| 
       165 
167 
     | 
    
         
             
                # dir<String>:: the directory to move to.
         
     | 
| 
       166 
168 
     | 
    
         
             
                # config<Hash>:: give :verbose => true to log and use padding.
         
     | 
| 
         @@ -173,22 +175,24 @@ class Thor 
     | 
|
| 
       173 
175 
     | 
    
         
             
                  shell.padding += 1 if verbose
         
     | 
| 
       174 
176 
     | 
    
         
             
                  @destination_stack.push File.expand_path(dir, destination_root)
         
     | 
| 
       175 
177 
     | 
    
         | 
| 
       176 
     | 
    
         
            -
                  # If the directory  
     | 
| 
      
 178 
     | 
    
         
            +
                  # If the directory doesn't exist and we're not pretending
         
     | 
| 
       177 
179 
     | 
    
         
             
                  if !File.exist?(destination_root) && !pretend
         
     | 
| 
       178 
180 
     | 
    
         
             
                    require "fileutils"
         
     | 
| 
       179 
181 
     | 
    
         
             
                    FileUtils.mkdir_p(destination_root)
         
     | 
| 
       180 
182 
     | 
    
         
             
                  end
         
     | 
| 
       181 
183 
     | 
    
         | 
| 
      
 184 
     | 
    
         
            +
                  result = nil
         
     | 
| 
       182 
185 
     | 
    
         
             
                  if pretend
         
     | 
| 
       183 
186 
     | 
    
         
             
                    # In pretend mode, just yield down to the block
         
     | 
| 
       184 
     | 
    
         
            -
                    block.arity == 1 ? yield(destination_root) : yield
         
     | 
| 
      
 187 
     | 
    
         
            +
                    result = block.arity == 1 ? yield(destination_root) : yield
         
     | 
| 
       185 
188 
     | 
    
         
             
                  else
         
     | 
| 
       186 
189 
     | 
    
         
             
                    require "fileutils"
         
     | 
| 
       187 
     | 
    
         
            -
                    FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
         
     | 
| 
      
 190 
     | 
    
         
            +
                    FileUtils.cd(destination_root) { result = block.arity == 1 ? yield(destination_root) : yield }
         
     | 
| 
       188 
191 
     | 
    
         
             
                  end
         
     | 
| 
       189 
192 
     | 
    
         | 
| 
       190 
193 
     | 
    
         
             
                  @destination_stack.pop
         
     | 
| 
       191 
194 
     | 
    
         
             
                  shell.padding -= 1 if verbose
         
     | 
| 
      
 195 
     | 
    
         
            +
                  result
         
     | 
| 
       192 
196 
     | 
    
         
             
                end
         
     | 
| 
       193 
197 
     | 
    
         | 
| 
       194 
198 
     | 
    
         
             
                # Goes to the root and execute the given block.
         
     | 
| 
         @@ -221,7 +225,7 @@ class Thor 
     | 
|
| 
       221 
225 
     | 
    
         
             
                    require "open-uri"
         
     | 
| 
       222 
226 
     | 
    
         
             
                    URI.open(path, "Accept" => "application/x-thor-template", &:read)
         
     | 
| 
       223 
227 
     | 
    
         
             
                  else
         
     | 
| 
       224 
     | 
    
         
            -
                    open(path, &:read)
         
     | 
| 
      
 228 
     | 
    
         
            +
                    File.open(path, &:read)
         
     | 
| 
       225 
229 
     | 
    
         
             
                  end
         
     | 
| 
       226 
230 
     | 
    
         | 
| 
       227 
231 
     | 
    
         
             
                  instance_eval(contents, path)
         
     | 
| 
         @@ -280,7 +284,7 @@ class Thor 
     | 
|
| 
       280 
284 
     | 
    
         
             
                #
         
     | 
| 
       281 
285 
     | 
    
         
             
                def run_ruby_script(command, config = {})
         
     | 
| 
       282 
286 
     | 
    
         
             
                  return unless behavior == :invoke
         
     | 
| 
       283 
     | 
    
         
            -
                  run command, config.merge(: 
     | 
| 
      
 287 
     | 
    
         
            +
                  run command, config.merge(with: Thor::Util.ruby_command)
         
     | 
| 
       284 
288 
     | 
    
         
             
                end
         
     | 
| 
       285 
289 
     | 
    
         | 
| 
       286 
290 
     | 
    
         
             
                # Run a thor command. A hash of options can be given and it's converted to
         
     | 
| 
         @@ -311,7 +315,7 @@ class Thor 
     | 
|
| 
       311 
315 
     | 
    
         
             
                  args.push Thor::Options.to_switches(config)
         
     | 
| 
       312 
316 
     | 
    
         
             
                  command = args.join(" ").strip
         
     | 
| 
       313 
317 
     | 
    
         | 
| 
       314 
     | 
    
         
            -
                  run command, : 
     | 
| 
      
 318 
     | 
    
         
            +
                  run command, with: :thor, verbose: verbose, pretend: pretend, capture: capture
         
     | 
| 
       315 
319 
     | 
    
         
             
                end
         
     | 
| 
       316 
320 
     | 
    
         | 
| 
       317 
321 
     | 
    
         
             
              protected
         
     | 
| 
         @@ -319,7 +323,7 @@ class Thor 
     | 
|
| 
       319 
323 
     | 
    
         
             
                # Allow current root to be shared between invocations.
         
     | 
| 
       320 
324 
     | 
    
         
             
                #
         
     | 
| 
       321 
325 
     | 
    
         
             
                def _shared_configuration #:nodoc:
         
     | 
| 
       322 
     | 
    
         
            -
                  super.merge!(: 
     | 
| 
      
 326 
     | 
    
         
            +
                  super.merge!(destination_root: destination_root)
         
     | 
| 
       323 
327 
     | 
    
         
             
                end
         
     | 
| 
       324 
328 
     | 
    
         | 
| 
       325 
329 
     | 
    
         
             
                def _cleanup_options_and_set(options, key) #:nodoc:
         
     | 
    
        data/lib/thor/base.rb
    CHANGED
    
    | 
         @@ -24,9 +24,9 @@ class Thor 
     | 
|
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
              class << self
         
     | 
| 
       26 
26 
     | 
    
         
             
                def deprecation_warning(message) #:nodoc:
         
     | 
| 
       27 
     | 
    
         
            -
                  unless ENV[ 
     | 
| 
      
 27 
     | 
    
         
            +
                  unless ENV["THOR_SILENCE_DEPRECATION"]
         
     | 
| 
       28 
28 
     | 
    
         
             
                    warn "Deprecation warning: #{message}\n" +
         
     | 
| 
       29 
     | 
    
         
            -
                       
     | 
| 
      
 29 
     | 
    
         
            +
                      "You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION."
         
     | 
| 
       30 
30 
     | 
    
         
             
                  end
         
     | 
| 
       31 
31 
     | 
    
         
             
                end
         
     | 
| 
       32 
32 
     | 
    
         
             
              end
         
     | 
| 
         @@ -60,6 +60,7 @@ class Thor 
     | 
|
| 
       60 
60 
     | 
    
         | 
| 
       61 
61 
     | 
    
         
             
                  command_options = config.delete(:command_options) # hook for start
         
     | 
| 
       62 
62 
     | 
    
         
             
                  parse_options = parse_options.merge(command_options) if command_options
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
       63 
64 
     | 
    
         
             
                  if local_options.is_a?(Array)
         
     | 
| 
       64 
65 
     | 
    
         
             
                    array_options = local_options
         
     | 
| 
       65 
66 
     | 
    
         
             
                    hash_options = {}
         
     | 
| 
         @@ -73,9 +74,24 @@ class Thor 
     | 
|
| 
       73 
74 
     | 
    
         
             
                  # Let Thor::Options parse the options first, so it can remove
         
     | 
| 
       74 
75 
     | 
    
         
             
                  # declared options from the array. This will leave us with
         
     | 
| 
       75 
76 
     | 
    
         
             
                  # a list of arguments that weren't declared.
         
     | 
| 
       76 
     | 
    
         
            -
                   
     | 
| 
       77 
     | 
    
         
            -
                   
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
      
 77 
     | 
    
         
            +
                  current_command = config[:current_command]
         
     | 
| 
      
 78 
     | 
    
         
            +
                  stop_on_unknown = self.class.stop_on_unknown_option? current_command
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # Give a relation of options.
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # After parsing, Thor::Options check whether right relations are kept
         
     | 
| 
      
 82 
     | 
    
         
            +
                  relations = if current_command.nil?
         
     | 
| 
      
 83 
     | 
    
         
            +
                    {exclusive_option_names: [], at_least_one_option_names: []}
         
     | 
| 
      
 84 
     | 
    
         
            +
                  else
         
     | 
| 
      
 85 
     | 
    
         
            +
                    current_command.options_relation
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  self.class.class_exclusive_option_names.map { |n| relations[:exclusive_option_names] << n }
         
     | 
| 
      
 89 
     | 
    
         
            +
                  self.class.class_at_least_one_option_names.map { |n| relations[:at_least_one_option_names] << n }
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  disable_required_check = self.class.disable_required_check? current_command
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check, relations)
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
       79 
95 
     | 
    
         
             
                  self.options = opts.parse(array_options)
         
     | 
| 
       80 
96 
     | 
    
         
             
                  self.options = config[:class_options].merge(options) if config[:class_options]
         
     | 
| 
       81 
97 
     | 
    
         | 
| 
         @@ -310,9 +326,92 @@ class Thor 
     | 
|
| 
       310 
326 
     | 
    
         
             
                  # :hide::     -- If you want to hide this option from the help.
         
     | 
| 
       311 
327 
     | 
    
         
             
                  #
         
     | 
| 
       312 
328 
     | 
    
         
             
                  def class_option(name, options = {})
         
     | 
| 
      
 329 
     | 
    
         
            +
                    unless [ Symbol, String ].any? { |klass| name.is_a?(klass) }
         
     | 
| 
      
 330 
     | 
    
         
            +
                      raise ArgumentError, "Expected a Symbol or String, got #{name.inspect}"
         
     | 
| 
      
 331 
     | 
    
         
            +
                    end
         
     | 
| 
       313 
332 
     | 
    
         
             
                    build_option(name, options, class_options)
         
     | 
| 
       314 
333 
     | 
    
         
             
                  end
         
     | 
| 
       315 
334 
     | 
    
         | 
| 
      
 335 
     | 
    
         
            +
                  # Adds and declares option group for exclusive options in the
         
     | 
| 
      
 336 
     | 
    
         
            +
                  # block and arguments. You can declare options as the outside of the block.
         
     | 
| 
      
 337 
     | 
    
         
            +
                  #
         
     | 
| 
      
 338 
     | 
    
         
            +
                  # ==== Parameters
         
     | 
| 
      
 339 
     | 
    
         
            +
                  # Array[Thor::Option.name]
         
     | 
| 
      
 340 
     | 
    
         
            +
                  #
         
     | 
| 
      
 341 
     | 
    
         
            +
                  # ==== Examples
         
     | 
| 
      
 342 
     | 
    
         
            +
                  #
         
     | 
| 
      
 343 
     | 
    
         
            +
                  #   class_exclusive do
         
     | 
| 
      
 344 
     | 
    
         
            +
                  #     class_option :one
         
     | 
| 
      
 345 
     | 
    
         
            +
                  #     class_option :two
         
     | 
| 
      
 346 
     | 
    
         
            +
                  #    end
         
     | 
| 
      
 347 
     | 
    
         
            +
                  #
         
     | 
| 
      
 348 
     | 
    
         
            +
                  # Or
         
     | 
| 
      
 349 
     | 
    
         
            +
                  #
         
     | 
| 
      
 350 
     | 
    
         
            +
                  #   class_option :one
         
     | 
| 
      
 351 
     | 
    
         
            +
                  #   class_option :two
         
     | 
| 
      
 352 
     | 
    
         
            +
                  #   class_exclusive :one, :two
         
     | 
| 
      
 353 
     | 
    
         
            +
                  #
         
     | 
| 
      
 354 
     | 
    
         
            +
                  # If you give "--one" and "--two" at the same time ExclusiveArgumentsError
         
     | 
| 
      
 355 
     | 
    
         
            +
                  # will be raised.
         
     | 
| 
      
 356 
     | 
    
         
            +
                  #
         
     | 
| 
      
 357 
     | 
    
         
            +
                  def class_exclusive(*args, &block)
         
     | 
| 
      
 358 
     | 
    
         
            +
                    register_options_relation_for(:class_options,
         
     | 
| 
      
 359 
     | 
    
         
            +
                                                  :class_exclusive_option_names, *args, &block)
         
     | 
| 
      
 360 
     | 
    
         
            +
                  end
         
     | 
| 
      
 361 
     | 
    
         
            +
             
     | 
| 
      
 362 
     | 
    
         
            +
                  # Adds and declares option group for required at least one of options in the
         
     | 
| 
      
 363 
     | 
    
         
            +
                  # block and arguments. You can declare options as the outside of the block.
         
     | 
| 
      
 364 
     | 
    
         
            +
                  #
         
     | 
| 
      
 365 
     | 
    
         
            +
                  # ==== Examples
         
     | 
| 
      
 366 
     | 
    
         
            +
                  #
         
     | 
| 
      
 367 
     | 
    
         
            +
                  #   class_at_least_one do
         
     | 
| 
      
 368 
     | 
    
         
            +
                  #     class_option :one
         
     | 
| 
      
 369 
     | 
    
         
            +
                  #     class_option :two
         
     | 
| 
      
 370 
     | 
    
         
            +
                  #    end
         
     | 
| 
      
 371 
     | 
    
         
            +
                  #
         
     | 
| 
      
 372 
     | 
    
         
            +
                  # Or
         
     | 
| 
      
 373 
     | 
    
         
            +
                  #
         
     | 
| 
      
 374 
     | 
    
         
            +
                  #   class_option :one
         
     | 
| 
      
 375 
     | 
    
         
            +
                  #   class_option :two
         
     | 
| 
      
 376 
     | 
    
         
            +
                  #   class_at_least_one :one, :two
         
     | 
| 
      
 377 
     | 
    
         
            +
                  #
         
     | 
| 
      
 378 
     | 
    
         
            +
                  # If you do not give "--one" and "--two" AtLeastOneRequiredArgumentError
         
     | 
| 
      
 379 
     | 
    
         
            +
                  # will be raised.
         
     | 
| 
      
 380 
     | 
    
         
            +
                  #
         
     | 
| 
      
 381 
     | 
    
         
            +
                  # You can use class_at_least_one and class_exclusive at the same time.
         
     | 
| 
      
 382 
     | 
    
         
            +
                  #
         
     | 
| 
      
 383 
     | 
    
         
            +
                  #    class_exclusive do
         
     | 
| 
      
 384 
     | 
    
         
            +
                  #      class_at_least_one do
         
     | 
| 
      
 385 
     | 
    
         
            +
                  #        class_option :one
         
     | 
| 
      
 386 
     | 
    
         
            +
                  #        class_option :two
         
     | 
| 
      
 387 
     | 
    
         
            +
                  #      end
         
     | 
| 
      
 388 
     | 
    
         
            +
                  #    end
         
     | 
| 
      
 389 
     | 
    
         
            +
                  #
         
     | 
| 
      
 390 
     | 
    
         
            +
                  # Then it is required either only one of "--one" or "--two".
         
     | 
| 
      
 391 
     | 
    
         
            +
                  #
         
     | 
| 
      
 392 
     | 
    
         
            +
                  def class_at_least_one(*args, &block)
         
     | 
| 
      
 393 
     | 
    
         
            +
                    register_options_relation_for(:class_options,
         
     | 
| 
      
 394 
     | 
    
         
            +
                                                  :class_at_least_one_option_names, *args, &block)
         
     | 
| 
      
 395 
     | 
    
         
            +
                  end
         
     | 
| 
      
 396 
     | 
    
         
            +
             
     | 
| 
      
 397 
     | 
    
         
            +
                  # Returns this class exclusive options array set, looking up in the ancestors chain.
         
     | 
| 
      
 398 
     | 
    
         
            +
                  #
         
     | 
| 
      
 399 
     | 
    
         
            +
                  # ==== Returns
         
     | 
| 
      
 400 
     | 
    
         
            +
                  # Array[Array[Thor::Option.name]]
         
     | 
| 
      
 401 
     | 
    
         
            +
                  #
         
     | 
| 
      
 402 
     | 
    
         
            +
                  def class_exclusive_option_names
         
     | 
| 
      
 403 
     | 
    
         
            +
                    @class_exclusive_option_names ||= from_superclass(:class_exclusive_option_names, [])
         
     | 
| 
      
 404 
     | 
    
         
            +
                  end
         
     | 
| 
      
 405 
     | 
    
         
            +
             
     | 
| 
      
 406 
     | 
    
         
            +
                  # Returns this class at least one of required options array set, looking up in the ancestors chain.
         
     | 
| 
      
 407 
     | 
    
         
            +
                  #
         
     | 
| 
      
 408 
     | 
    
         
            +
                  # ==== Returns
         
     | 
| 
      
 409 
     | 
    
         
            +
                  # Array[Array[Thor::Option.name]]
         
     | 
| 
      
 410 
     | 
    
         
            +
                  #
         
     | 
| 
      
 411 
     | 
    
         
            +
                  def class_at_least_one_option_names
         
     | 
| 
      
 412 
     | 
    
         
            +
                    @class_at_least_one_option_names ||= from_superclass(:class_at_least_one_option_names, [])
         
     | 
| 
      
 413 
     | 
    
         
            +
                  end
         
     | 
| 
      
 414 
     | 
    
         
            +
             
     | 
| 
       316 
415 
     | 
    
         
             
                  # Removes a previous defined argument. If :undefine is given, undefine
         
     | 
| 
       317 
416 
     | 
    
         
             
                  # accessors as well.
         
     | 
| 
       318 
417 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -506,7 +605,7 @@ class Thor 
     | 
|
| 
       506 
605 
     | 
    
         
             
                  #
         
     | 
| 
       507 
606 
     | 
    
         
             
                  def public_command(*names)
         
     | 
| 
       508 
607 
     | 
    
         
             
                    names.each do |name|
         
     | 
| 
       509 
     | 
    
         
            -
                      class_eval "def #{name}(*); super end"
         
     | 
| 
      
 608 
     | 
    
         
            +
                      class_eval "def #{name}(*); super end", __FILE__, __LINE__
         
     | 
| 
       510 
609 
     | 
    
         
             
                    end
         
     | 
| 
       511 
610 
     | 
    
         
             
                  end
         
     | 
| 
       512 
611 
     | 
    
         
             
                  alias_method :public_task, :public_command
         
     | 
| 
         @@ -558,20 +657,19 @@ class Thor 
     | 
|
| 
       558 
657 
     | 
    
         
             
                    return if options.empty?
         
     | 
| 
       559 
658 
     | 
    
         | 
| 
       560 
659 
     | 
    
         
             
                    list = []
         
     | 
| 
       561 
     | 
    
         
            -
                    padding = options.map { |o| o. 
     | 
| 
       562 
     | 
    
         
            -
             
     | 
| 
      
 660 
     | 
    
         
            +
                    padding = options.map { |o| o.aliases_for_usage.size }.max.to_i
         
     | 
| 
       563 
661 
     | 
    
         
             
                    options.each do |option|
         
     | 
| 
       564 
662 
     | 
    
         
             
                      next if option.hide
         
     | 
| 
       565 
663 
     | 
    
         
             
                      item = [option.usage(padding)]
         
     | 
| 
       566 
664 
     | 
    
         
             
                      item.push(option.description ? "# #{option.description}" : "")
         
     | 
| 
       567 
665 
     | 
    
         | 
| 
       568 
666 
     | 
    
         
             
                      list << item
         
     | 
| 
       569 
     | 
    
         
            -
                      list << ["", "# Default: #{option. 
     | 
| 
       570 
     | 
    
         
            -
                      list << ["", "# Possible values: #{option. 
     | 
| 
      
 667 
     | 
    
         
            +
                      list << ["", "# Default: #{option.print_default}"] if option.show_default?
         
     | 
| 
      
 668 
     | 
    
         
            +
                      list << ["", "# Possible values: #{option.enum_to_s}"] if option.enum
         
     | 
| 
       571 
669 
     | 
    
         
             
                    end
         
     | 
| 
       572 
670 
     | 
    
         | 
| 
       573 
671 
     | 
    
         
             
                    shell.say(group_name ? "#{group_name} options:" : "Options:")
         
     | 
| 
       574 
     | 
    
         
            -
                    shell.print_table(list, : 
     | 
| 
      
 672 
     | 
    
         
            +
                    shell.print_table(list, indent: 2)
         
     | 
| 
       575 
673 
     | 
    
         
             
                    shell.say ""
         
     | 
| 
       576 
674 
     | 
    
         
             
                  end
         
     | 
| 
       577 
675 
     | 
    
         | 
| 
         @@ -588,7 +686,7 @@ class Thor 
     | 
|
| 
       588 
686 
     | 
    
         
             
                  # options<Hash>:: Described in both class_option and method_option.
         
     | 
| 
       589 
687 
     | 
    
         
             
                  # scope<Hash>:: Options hash that is being built up
         
     | 
| 
       590 
688 
     | 
    
         
             
                  def build_option(name, options, scope) #:nodoc:
         
     | 
| 
       591 
     | 
    
         
            -
                    scope[name] = Thor::Option.new(name, {: 
     | 
| 
      
 689 
     | 
    
         
            +
                    scope[name] = Thor::Option.new(name, {check_default_type: check_default_type}.merge!(options))
         
     | 
| 
       592 
690 
     | 
    
         
             
                  end
         
     | 
| 
       593 
691 
     | 
    
         | 
| 
       594 
692 
     | 
    
         
             
                  # Receives a hash of options, parse them and add to the scope. This is a
         
     | 
| 
         @@ -610,7 +708,7 @@ class Thor 
     | 
|
| 
       610 
708 
     | 
    
         
             
                  def find_and_refresh_command(name) #:nodoc:
         
     | 
| 
       611 
709 
     | 
    
         
             
                    if commands[name.to_s]
         
     | 
| 
       612 
710 
     | 
    
         
             
                      commands[name.to_s]
         
     | 
| 
       613 
     | 
    
         
            -
                    elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
         
     | 
| 
      
 711 
     | 
    
         
            +
                    elsif command = all_commands[name.to_s] # rubocop:disable Lint/AssignmentInCondition
         
     | 
| 
       614 
712 
     | 
    
         
             
                      commands[name.to_s] = command.clone
         
     | 
| 
       615 
713 
     | 
    
         
             
                    else
         
     | 
| 
       616 
714 
     | 
    
         
             
                      raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
         
     | 
| 
         @@ -618,7 +716,7 @@ class Thor 
     | 
|
| 
       618 
716 
     | 
    
         
             
                  end
         
     | 
| 
       619 
717 
     | 
    
         
             
                  alias_method :find_and_refresh_task, :find_and_refresh_command
         
     | 
| 
       620 
718 
     | 
    
         | 
| 
       621 
     | 
    
         
            -
                  #  
     | 
| 
      
 719 
     | 
    
         
            +
                  # Every time someone inherits from a Thor class, register the klass
         
     | 
| 
       622 
720 
     | 
    
         
             
                  # and file into baseclass.
         
     | 
| 
       623 
721 
     | 
    
         
             
                  def inherited(klass)
         
     | 
| 
       624 
722 
     | 
    
         
             
                    super(klass)
         
     | 
| 
         @@ -694,6 +792,34 @@ class Thor 
     | 
|
| 
       694 
792 
     | 
    
         
             
                  def dispatch(command, given_args, given_opts, config) #:nodoc:
         
     | 
| 
       695 
793 
     | 
    
         
             
                    raise NotImplementedError
         
     | 
| 
       696 
794 
     | 
    
         
             
                  end
         
     | 
| 
      
 795 
     | 
    
         
            +
             
     | 
| 
      
 796 
     | 
    
         
            +
                  # Register a relation of options for target(method_option/class_option)
         
     | 
| 
      
 797 
     | 
    
         
            +
                  # by args and block.
         
     | 
| 
      
 798 
     | 
    
         
            +
                  def register_options_relation_for(target, relation, *args, &block) # :nodoc:
         
     | 
| 
      
 799 
     | 
    
         
            +
                    opt = args.pop if args.last.is_a? Hash
         
     | 
| 
      
 800 
     | 
    
         
            +
                    opt ||= {}
         
     | 
| 
      
 801 
     | 
    
         
            +
                    names = args.map{ |arg| arg.to_s }
         
     | 
| 
      
 802 
     | 
    
         
            +
                    names += built_option_names(target, opt, &block) if block_given?
         
     | 
| 
      
 803 
     | 
    
         
            +
                    command_scope_member(relation, opt) << names
         
     | 
| 
      
 804 
     | 
    
         
            +
                  end
         
     | 
| 
      
 805 
     | 
    
         
            +
             
     | 
| 
      
 806 
     | 
    
         
            +
                  # Get target(method_options or class_options) options
         
     | 
| 
      
 807 
     | 
    
         
            +
                  # of before and after by block evaluation.
         
     | 
| 
      
 808 
     | 
    
         
            +
                  def built_option_names(target, opt = {}, &block) # :nodoc:
         
     | 
| 
      
 809 
     | 
    
         
            +
                    before = command_scope_member(target, opt).map{ |k,v| v.name }
         
     | 
| 
      
 810 
     | 
    
         
            +
                    instance_eval(&block)
         
     | 
| 
      
 811 
     | 
    
         
            +
                    after  = command_scope_member(target, opt).map{ |k,v| v.name }
         
     | 
| 
      
 812 
     | 
    
         
            +
                    after - before
         
     | 
| 
      
 813 
     | 
    
         
            +
                  end
         
     | 
| 
      
 814 
     | 
    
         
            +
             
     | 
| 
      
 815 
     | 
    
         
            +
                  # Get command scope member by name.
         
     | 
| 
      
 816 
     | 
    
         
            +
                  def command_scope_member(name, options = {}) # :nodoc:
         
     | 
| 
      
 817 
     | 
    
         
            +
                    if options[:for]
         
     | 
| 
      
 818 
     | 
    
         
            +
                      find_and_refresh_command(options[:for]).send(name)
         
     | 
| 
      
 819 
     | 
    
         
            +
                    else
         
     | 
| 
      
 820 
     | 
    
         
            +
                      send(name)
         
     | 
| 
      
 821 
     | 
    
         
            +
                    end
         
     | 
| 
      
 822 
     | 
    
         
            +
                  end
         
     | 
| 
       697 
823 
     | 
    
         
             
                end
         
     | 
| 
       698 
824 
     | 
    
         
             
              end
         
     | 
| 
       699 
825 
     | 
    
         
             
            end
         
     |