thor 0.20.3 → 1.3.2
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.
Potentially problematic release.
This version of thor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +3 -9
- data/lib/thor/actions/create_file.rb +4 -3
- data/lib/thor/actions/create_link.rb +3 -2
- data/lib/thor/actions/directory.rb +8 -18
- data/lib/thor/actions/empty_directory.rb +1 -1
- data/lib/thor/actions/file_manipulation.rb +22 -24
- data/lib/thor/actions/inject_into_file.rb +34 -13
- data/lib/thor/actions.rb +39 -30
- data/lib/thor/base.rb +196 -49
- data/lib/thor/command.rb +34 -18
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +10 -0
- data/lib/thor/error.rb +14 -22
- data/lib/thor/group.rb +13 -2
- data/lib/thor/invocation.rb +2 -1
- data/lib/thor/line_editor/basic.rb +1 -1
- data/lib/thor/line_editor/readline.rb +6 -6
- data/lib/thor/line_editor.rb +2 -2
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser/argument.rb +17 -1
- data/lib/thor/parser/arguments.rb +35 -15
- data/lib/thor/parser/option.rb +45 -13
- data/lib/thor/parser/options.rb +79 -11
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +3 -2
- data/lib/thor/runner.rb +43 -32
- data/lib/thor/shell/basic.rb +68 -162
- data/lib/thor/shell/color.rb +9 -43
- data/lib/thor/shell/column_printer.rb +29 -0
- data/lib/thor/shell/html.rb +7 -49
- 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 +5 -5
- data/lib/thor/util.rb +25 -8
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +182 -17
- data/thor.gemspec +22 -10
- metadata +25 -11
- data/CHANGELOG.md +0 -204
- data/lib/thor/core_ext/io_binary_read.rb +0 -12
- data/lib/thor/core_ext/ordered_hash.rb +0 -129
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 9b49f263d36f84d82f17f16852671ff9e2f529ea0adec4b664a2b8660923d091
         | 
| 4 | 
            +
              data.tar.gz: 2df9cade7c368e064377ec0f38737d5b379b38265f23d878cb4976588b94564c
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c4cca8a5e388509dd8a45a6484c13fa80d89f15c7bfde65ccc3d48d3f86c299269ce6019af1dfe3d2f6f172aff5ae7d7ef8f49ca69de189dcd721d5c9d48269f
         | 
| 7 | 
            +
              data.tar.gz: 85b9b4834a91e7fab98ee9f555461cee740bebcbcee3e0efffc9d117d1b8dce6f9967db594225cd32a3d82462b5edc2d6e4c77bb4ef799d6253f3e66f4f88042
         | 
    
        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
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            require_relative "empty_directory"
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Thor
         | 
| 4 4 | 
             
              module Actions
         | 
| @@ -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
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            require_relative "create_file"
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Thor
         | 
| 4 4 | 
             
              module Actions
         | 
| @@ -33,7 +33,8 @@ class Thor | |
| 33 33 | 
             
                  # Boolean:: true if it is identical, false otherwise.
         | 
| 34 34 | 
             
                  #
         | 
| 35 35 | 
             
                  def identical?
         | 
| 36 | 
            -
                     | 
| 36 | 
            +
                    source = File.expand_path(render, File.dirname(destination))
         | 
| 37 | 
            +
                    exists? && File.identical?(source, destination)
         | 
| 37 38 | 
             
                  end
         | 
| 38 39 |  | 
| 39 40 | 
             
                  def invoke!
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            require_relative "empty_directory"
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Thor
         | 
| 4 4 | 
             
              module Actions
         | 
| @@ -56,9 +56,9 @@ class Thor | |
| 56 56 | 
             
                  attr_reader :source
         | 
| 57 57 |  | 
| 58 58 | 
             
                  def initialize(base, source, destination = nil, config = {}, &block)
         | 
| 59 | 
            -
                    @source = File.expand_path(base.find_in_source_paths(source.to_s))
         | 
| 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!
         | 
| @@ -96,22 +96,12 @@ class Thor | |
| 96 96 | 
             
                    end
         | 
| 97 97 | 
             
                  end
         | 
| 98 98 |  | 
| 99 | 
            -
                   | 
| 100 | 
            -
                     | 
| 101 | 
            -
             | 
| 102 | 
            -
                    end
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                    def files(lookup)
         | 
| 105 | 
            -
                      Dir[lookup]
         | 
| 106 | 
            -
                    end
         | 
| 107 | 
            -
                  else
         | 
| 108 | 
            -
                    def file_level_lookup(previous_lookup)
         | 
| 109 | 
            -
                      File.join(previous_lookup, "*")
         | 
| 110 | 
            -
                    end
         | 
| 99 | 
            +
                  def file_level_lookup(previous_lookup)
         | 
| 100 | 
            +
                    File.join(previous_lookup, "*")
         | 
| 101 | 
            +
                  end
         | 
| 111 102 |  | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
                    end
         | 
| 103 | 
            +
                  def files(lookup)
         | 
| 104 | 
            +
                    Dir.glob(lookup, File::FNM_DOTMATCH)
         | 
| 115 105 | 
             
                  end
         | 
| 116 106 | 
             
                end
         | 
| 117 107 | 
             
              end
         | 
| @@ -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 | 
             
                #
         | 
| @@ -23,14 +22,14 @@ class Thor | |
| 23 22 | 
             
                  destination = args.first || source
         | 
| 24 23 | 
             
                  source = File.expand_path(find_in_source_paths(source.to_s))
         | 
| 25 24 |  | 
| 26 | 
            -
                  create_file destination, nil, config do
         | 
| 25 | 
            +
                  resulting_destination = create_file destination, nil, config do
         | 
| 27 26 | 
             
                    content = File.binread(source)
         | 
| 28 27 | 
             
                    content = yield(content) if block
         | 
| 29 28 | 
             
                    content
         | 
| 30 29 | 
             
                  end
         | 
| 31 30 | 
             
                  if config[:mode] == :preserve
         | 
| 32 31 | 
             
                    mode = File.stat(source).mode
         | 
| 33 | 
            -
                    chmod( | 
| 32 | 
            +
                    chmod(resulting_destination, mode, config)
         | 
| 34 33 | 
             
                  end
         | 
| 35 34 | 
             
                end
         | 
| 36 35 |  | 
| @@ -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
         | 
| @@ -80,14 +82,14 @@ class Thor | |
| 80 82 | 
             
                  config = args.last.is_a?(Hash) ? args.pop : {}
         | 
| 81 83 | 
             
                  destination = args.first
         | 
| 82 84 |  | 
| 83 | 
            -
                  if source =~ %r{^https?\://}
         | 
| 85 | 
            +
                  render = if source =~ %r{^https?\://}
         | 
| 84 86 | 
             
                    require "open-uri"
         | 
| 87 | 
            +
                    URI.send(:open, source, config.fetch(:http_headers, {})) { |input| input.binmode.read }
         | 
| 85 88 | 
             
                  else
         | 
| 86 89 | 
             
                    source = File.expand_path(find_in_source_paths(source.to_s))
         | 
| 90 | 
            +
                    File.open(source) { |input| input.binmode.read }
         | 
| 87 91 | 
             
                  end
         | 
| 88 92 |  | 
| 89 | 
            -
                  render = open(source) { |input| input.binmode.read }
         | 
| 90 | 
            -
             | 
| 91 93 | 
             
                  destination ||= if block_given?
         | 
| 92 94 | 
             
                    block.arity == 1 ? yield(render) : yield
         | 
| 93 95 | 
             
                  else
         | 
| @@ -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 | 
             
                #
         | 
| @@ -251,7 +248,8 @@ class Thor | |
| 251 248 | 
             
                # path<String>:: path of the file to be changed
         | 
| 252 249 | 
             
                # flag<Regexp|String>:: the regexp or string to be replaced
         | 
| 253 250 | 
             
                # replacement<String>:: the replacement, can be also given as a block
         | 
| 254 | 
            -
                # config<Hash>:: give :verbose => false to not log the status | 
| 251 | 
            +
                # config<Hash>:: give :verbose => false to not log the status, and
         | 
| 252 | 
            +
                #                :force => true, to force the replacement regardless of runner behavior.
         | 
| 255 253 | 
             
                #
         | 
| 256 254 | 
             
                # ==== Example
         | 
| 257 255 | 
             
                #
         | 
| @@ -262,9 +260,10 @@ class Thor | |
| 262 260 | 
             
                #   end
         | 
| 263 261 | 
             
                #
         | 
| 264 262 | 
             
                def gsub_file(path, flag, *args, &block)
         | 
| 265 | 
            -
                  return unless behavior == :invoke
         | 
| 266 263 | 
             
                  config = args.last.is_a?(Hash) ? args.pop : {}
         | 
| 267 264 |  | 
| 265 | 
            +
                  return unless behavior == :invoke || config.fetch(:force, false)
         | 
| 266 | 
            +
             | 
| 268 267 | 
             
                  path = File.expand_path(path, destination_root)
         | 
| 269 268 | 
             
                  say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
         | 
| 270 269 |  | 
| @@ -275,9 +274,8 @@ class Thor | |
| 275 274 | 
             
                  end
         | 
| 276 275 | 
             
                end
         | 
| 277 276 |  | 
| 278 | 
            -
                # Uncomment all lines matching a given regex. | 
| 279 | 
            -
                #  | 
| 280 | 
            -
                # between the comment hash and the beginning of the line.
         | 
| 277 | 
            +
                # Uncomment all lines matching a given regex. Preserves indentation before
         | 
| 278 | 
            +
                # the comment hash and removes the hash and any immediate following space.
         | 
| 281 279 | 
             
                #
         | 
| 282 280 | 
             
                # ==== Parameters
         | 
| 283 281 | 
             
                # path<String>:: path of the file to be changed
         | 
| @@ -291,7 +289,7 @@ class Thor | |
| 291 289 | 
             
                def uncomment_lines(path, flag, *args)
         | 
| 292 290 | 
             
                  flag = flag.respond_to?(:source) ? flag.source : flag
         | 
| 293 291 |  | 
| 294 | 
            -
                  gsub_file(path, /^(\s*)#[[:blank:]] | 
| 292 | 
            +
                  gsub_file(path, /^(\s*)#[[:blank:]]?(.*#{flag})/, '\1\2', *args)
         | 
| 295 293 | 
             
                end
         | 
| 296 294 |  | 
| 297 295 | 
             
                # Comment all lines matching a given regex.  It will leave the space
         | 
| @@ -329,7 +327,7 @@ class Thor | |
| 329 327 | 
             
                  path = File.expand_path(path, destination_root)
         | 
| 330 328 |  | 
| 331 329 | 
             
                  say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
         | 
| 332 | 
            -
                  if !options[:pretend] && File.exist?(path)
         | 
| 330 | 
            +
                  if !options[:pretend] && (File.exist?(path) || File.symlink?(path))
         | 
| 333 331 | 
             
                    require "fileutils"
         | 
| 334 332 | 
             
                    ::FileUtils.rm_rf(path)
         | 
| 335 333 | 
             
                  end
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            require_relative "empty_directory"
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Thor
         | 
| 4 4 | 
             
              module Actions
         | 
| @@ -21,9 +21,14 @@ class Thor | |
| 21 21 | 
             
                #     gems.split(" ").map{ |gem| "  config.gem :#{gem}" }.join("\n")
         | 
| 22 22 | 
             
                #   end
         | 
| 23 23 | 
             
                #
         | 
| 24 | 
            +
                WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
         | 
| 25 | 
            +
             | 
| 24 26 | 
             
                def insert_into_file(destination, *args, &block)
         | 
| 25 27 | 
             
                  data = block_given? ? block : args.shift
         | 
| 26 | 
            -
             | 
| 28 | 
            +
             | 
| 29 | 
            +
                  config = args.shift || {}
         | 
| 30 | 
            +
                  config[:after] = /\z/ unless config.key?(:before) || config.key?(:after)
         | 
| 31 | 
            +
             | 
| 27 32 | 
             
                  action InjectIntoFile.new(self, destination, data, config)
         | 
| 28 33 | 
             
                end
         | 
| 29 34 | 
             
                alias_method :inject_into_file, :insert_into_file
         | 
| @@ -32,7 +37,7 @@ class Thor | |
| 32 37 | 
             
                  attr_reader :replacement, :flag, :behavior
         | 
| 33 38 |  | 
| 34 39 | 
             
                  def initialize(base, destination, data, config)
         | 
| 35 | 
            -
                    super(base, destination, {: | 
| 40 | 
            +
                    super(base, destination, {verbose: true}.merge(config))
         | 
| 36 41 |  | 
| 37 42 | 
             
                    @behavior, @flag = if @config.key?(:after)
         | 
| 38 43 | 
             
                      [:after, @config.delete(:after)]
         | 
| @@ -45,8 +50,6 @@ class Thor | |
| 45 50 | 
             
                  end
         | 
| 46 51 |  | 
| 47 52 | 
             
                  def invoke!
         | 
| 48 | 
            -
                    say_status :invoke
         | 
| 49 | 
            -
             | 
| 50 53 | 
             
                    content = if @behavior == :after
         | 
| 51 54 | 
             
                      '\0' + replacement
         | 
| 52 55 | 
             
                    else
         | 
| @@ -54,7 +57,13 @@ class Thor | |
| 54 57 | 
             
                    end
         | 
| 55 58 |  | 
| 56 59 | 
             
                    if exists?
         | 
| 57 | 
            -
                      replace!(/#{flag}/, content, config[:force])
         | 
| 60 | 
            +
                      if replace!(/#{flag}/, content, config[:force])
         | 
| 61 | 
            +
                        say_status(:invoke)
         | 
| 62 | 
            +
                      elsif replacement_present?
         | 
| 63 | 
            +
                        say_status(:unchanged, color: :blue)
         | 
| 64 | 
            +
                      else
         | 
| 65 | 
            +
                        say_status(:unchanged, warning: WARNINGS[:unchanged_no_flag], color: :red)
         | 
| 66 | 
            +
                      end
         | 
| 58 67 | 
             
                    else
         | 
| 59 68 | 
             
                      unless pretend?
         | 
| 60 69 | 
             
                        raise Thor::Error, "The file #{ destination } does not appear to exist"
         | 
| @@ -78,7 +87,7 @@ class Thor | |
| 78 87 |  | 
| 79 88 | 
             
                protected
         | 
| 80 89 |  | 
| 81 | 
            -
                  def say_status(behavior)
         | 
| 90 | 
            +
                  def say_status(behavior, warning: nil, color: nil)
         | 
| 82 91 | 
             
                    status = if behavior == :invoke
         | 
| 83 92 | 
             
                      if flag == /\A/
         | 
| 84 93 | 
             
                        :prepend
         | 
| @@ -87,21 +96,33 @@ class Thor | |
| 87 96 | 
             
                      else
         | 
| 88 97 | 
             
                        :insert
         | 
| 89 98 | 
             
                      end
         | 
| 99 | 
            +
                    elsif warning
         | 
| 100 | 
            +
                      warning
         | 
| 101 | 
            +
                    elsif behavior == :unchanged
         | 
| 102 | 
            +
                      :unchanged
         | 
| 90 103 | 
             
                    else
         | 
| 91 104 | 
             
                      :subtract
         | 
| 92 105 | 
             
                    end
         | 
| 93 106 |  | 
| 94 | 
            -
                    super(status, config[:verbose])
         | 
| 107 | 
            +
                    super(status, (color || config[:verbose]))
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  def content
         | 
| 111 | 
            +
                    @content ||= File.read(destination)
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  def replacement_present?
         | 
| 115 | 
            +
                    content.include?(replacement)
         | 
| 95 116 | 
             
                  end
         | 
| 96 117 |  | 
| 97 118 | 
             
                  # Adds the content to the file.
         | 
| 98 119 | 
             
                  #
         | 
| 99 120 | 
             
                  def replace!(regexp, string, force)
         | 
| 100 | 
            -
                     | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
                       | 
| 104 | 
            -
                       | 
| 121 | 
            +
                    if force || !replacement_present?
         | 
| 122 | 
            +
                      success = content.gsub!(regexp, string)
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                      File.open(destination, "wb") { |file| file.write(content) } unless pretend?
         | 
| 125 | 
            +
                      success
         | 
| 105 126 | 
             
                    end
         | 
| 106 127 | 
             
                  end
         | 
| 107 128 | 
             
                end
         | 
    
        data/lib/thor/actions.rb
    CHANGED
    
    | @@ -1,17 +1,16 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            require "thor/actions/file_manipulation"
         | 
| 8 | 
            -
            require "thor/actions/inject_into_file"
         | 
| 1 | 
            +
            require_relative "actions/create_file"
         | 
| 2 | 
            +
            require_relative "actions/create_link"
         | 
| 3 | 
            +
            require_relative "actions/directory"
         | 
| 4 | 
            +
            require_relative "actions/empty_directory"
         | 
| 5 | 
            +
            require_relative "actions/file_manipulation"
         | 
| 6 | 
            +
            require_relative "actions/inject_into_file"
         | 
| 9 7 |  | 
| 10 8 | 
             
            class Thor
         | 
| 11 9 | 
             
              module Actions
         | 
| 12 10 | 
             
                attr_accessor :behavior
         | 
| 13 11 |  | 
| 14 12 | 
             
                def self.included(base) #:nodoc:
         | 
| 13 | 
            +
                  super(base)
         | 
| 15 14 | 
             
                  base.extend ClassMethods
         | 
| 16 15 | 
             
                end
         | 
| 17 16 |  | 
| @@ -47,17 +46,17 @@ class Thor | |
| 47 46 | 
             
                  # Add runtime options that help actions execution.
         | 
| 48 47 | 
             
                  #
         | 
| 49 48 | 
             
                  def add_runtime_options!
         | 
| 50 | 
            -
                    class_option :force, : | 
| 51 | 
            -
                                         : | 
| 49 | 
            +
                    class_option :force, type: :boolean, aliases: "-f", group: :runtime,
         | 
| 50 | 
            +
                                         desc: "Overwrite files that already exist"
         | 
| 52 51 |  | 
| 53 | 
            -
                    class_option :pretend, : | 
| 54 | 
            -
                                           : | 
| 52 | 
            +
                    class_option :pretend, type: :boolean, aliases: "-p", group: :runtime,
         | 
| 53 | 
            +
                                           desc: "Run but do not make any changes"
         | 
| 55 54 |  | 
| 56 | 
            -
                    class_option :quiet, : | 
| 57 | 
            -
                                         : | 
| 55 | 
            +
                    class_option :quiet, type: :boolean, aliases: "-q", group: :runtime,
         | 
| 56 | 
            +
                                         desc: "Suppress status output"
         | 
| 58 57 |  | 
| 59 | 
            -
                    class_option :skip, : | 
| 60 | 
            -
                                        : | 
| 58 | 
            +
                    class_option :skip, type: :boolean, aliases: "-s", group: :runtime,
         | 
| 59 | 
            +
                                        desc: "Skip files that already exist"
         | 
| 61 60 | 
             
                  end
         | 
| 62 61 | 
             
                end
         | 
| 63 62 |  | 
| @@ -114,9 +113,9 @@ class Thor | |
| 114 113 | 
             
                #
         | 
| 115 114 | 
             
                def relative_to_original_destination_root(path, remove_dot = true)
         | 
| 116 115 | 
             
                  root = @destination_stack[0]
         | 
| 117 | 
            -
                  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])
         | 
| 118 117 | 
             
                    path = path.dup
         | 
| 119 | 
            -
                    path[0...root.size] =  | 
| 118 | 
            +
                    path[0...root.size] = "."
         | 
| 120 119 | 
             
                    remove_dot ? (path[2..-1] || "") : path
         | 
| 121 120 | 
             
                  else
         | 
| 122 121 | 
             
                    path
         | 
| @@ -162,6 +161,8 @@ class Thor | |
| 162 161 | 
             
                # to the block you provide. The path is set back to the previous path when
         | 
| 163 162 | 
             
                # the method exits.
         | 
| 164 163 | 
             
                #
         | 
| 164 | 
            +
                # Returns the value yielded by the block.
         | 
| 165 | 
            +
                #
         | 
| 165 166 | 
             
                # ==== Parameters
         | 
| 166 167 | 
             
                # dir<String>:: the directory to move to.
         | 
| 167 168 | 
             
                # config<Hash>:: give :verbose => true to log and use padding.
         | 
| @@ -174,22 +175,24 @@ class Thor | |
| 174 175 | 
             
                  shell.padding += 1 if verbose
         | 
| 175 176 | 
             
                  @destination_stack.push File.expand_path(dir, destination_root)
         | 
| 176 177 |  | 
| 177 | 
            -
                  # If the directory  | 
| 178 | 
            +
                  # If the directory doesn't exist and we're not pretending
         | 
| 178 179 | 
             
                  if !File.exist?(destination_root) && !pretend
         | 
| 179 180 | 
             
                    require "fileutils"
         | 
| 180 181 | 
             
                    FileUtils.mkdir_p(destination_root)
         | 
| 181 182 | 
             
                  end
         | 
| 182 183 |  | 
| 184 | 
            +
                  result = nil
         | 
| 183 185 | 
             
                  if pretend
         | 
| 184 186 | 
             
                    # In pretend mode, just yield down to the block
         | 
| 185 | 
            -
                    block.arity == 1 ? yield(destination_root) : yield
         | 
| 187 | 
            +
                    result = block.arity == 1 ? yield(destination_root) : yield
         | 
| 186 188 | 
             
                  else
         | 
| 187 189 | 
             
                    require "fileutils"
         | 
| 188 | 
            -
                    FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
         | 
| 190 | 
            +
                    FileUtils.cd(destination_root) { result = block.arity == 1 ? yield(destination_root) : yield }
         | 
| 189 191 | 
             
                  end
         | 
| 190 192 |  | 
| 191 193 | 
             
                  @destination_stack.pop
         | 
| 192 194 | 
             
                  shell.padding -= 1 if verbose
         | 
| 195 | 
            +
                  result
         | 
| 193 196 | 
             
                end
         | 
| 194 197 |  | 
| 195 198 | 
             
                # Goes to the root and execute the given block.
         | 
| @@ -220,9 +223,9 @@ class Thor | |
| 220 223 |  | 
| 221 224 | 
             
                  contents = if is_uri
         | 
| 222 225 | 
             
                    require "open-uri"
         | 
| 223 | 
            -
                    open(path, "Accept" => "application/x-thor-template", &:read)
         | 
| 226 | 
            +
                    URI.open(path, "Accept" => "application/x-thor-template", &:read)
         | 
| 224 227 | 
             
                  else
         | 
| 225 | 
            -
                    open(path, &:read)
         | 
| 228 | 
            +
                    File.open(path, &:read)
         | 
| 226 229 | 
             
                  end
         | 
| 227 230 |  | 
| 228 231 | 
             
                  instance_eval(contents, path)
         | 
| @@ -257,13 +260,19 @@ class Thor | |
| 257 260 |  | 
| 258 261 | 
             
                  return if options[:pretend]
         | 
| 259 262 |  | 
| 260 | 
            -
                   | 
| 263 | 
            +
                  env_splat = [config[:env]] if config[:env]
         | 
| 261 264 |  | 
| 262 | 
            -
                  if config[: | 
| 263 | 
            -
                     | 
| 264 | 
            -
                     | 
| 265 | 
            +
                  if config[:capture]
         | 
| 266 | 
            +
                    require "open3"
         | 
| 267 | 
            +
                    result, status = Open3.capture2e(*env_splat, command.to_s)
         | 
| 268 | 
            +
                    success = status.success?
         | 
| 269 | 
            +
                  else
         | 
| 270 | 
            +
                    result = system(*env_splat, command.to_s)
         | 
| 271 | 
            +
                    success = result
         | 
| 265 272 | 
             
                  end
         | 
| 266 273 |  | 
| 274 | 
            +
                  abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?)
         | 
| 275 | 
            +
             | 
| 267 276 | 
             
                  result
         | 
| 268 277 | 
             
                end
         | 
| 269 278 |  | 
| @@ -275,7 +284,7 @@ class Thor | |
| 275 284 | 
             
                #
         | 
| 276 285 | 
             
                def run_ruby_script(command, config = {})
         | 
| 277 286 | 
             
                  return unless behavior == :invoke
         | 
| 278 | 
            -
                  run command, config.merge(: | 
| 287 | 
            +
                  run command, config.merge(with: Thor::Util.ruby_command)
         | 
| 279 288 | 
             
                end
         | 
| 280 289 |  | 
| 281 290 | 
             
                # Run a thor command. A hash of options can be given and it's converted to
         | 
| @@ -306,7 +315,7 @@ class Thor | |
| 306 315 | 
             
                  args.push Thor::Options.to_switches(config)
         | 
| 307 316 | 
             
                  command = args.join(" ").strip
         | 
| 308 317 |  | 
| 309 | 
            -
                  run command, : | 
| 318 | 
            +
                  run command, with: :thor, verbose: verbose, pretend: pretend, capture: capture
         | 
| 310 319 | 
             
                end
         | 
| 311 320 |  | 
| 312 321 | 
             
              protected
         | 
| @@ -314,7 +323,7 @@ class Thor | |
| 314 323 | 
             
                # Allow current root to be shared between invocations.
         | 
| 315 324 | 
             
                #
         | 
| 316 325 | 
             
                def _shared_configuration #:nodoc:
         | 
| 317 | 
            -
                  super.merge!(: | 
| 326 | 
            +
                  super.merge!(destination_root: destination_root)
         | 
| 318 327 | 
             
                end
         | 
| 319 328 |  | 
| 320 329 | 
             
                def _cleanup_options_and_set(options, key) #:nodoc:
         |