thor 0.16.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +15 -0
  3. data/README.md +23 -6
  4. data/bin/thor +1 -1
  5. data/lib/thor/actions/create_file.rb +34 -35
  6. data/lib/thor/actions/create_link.rb +9 -5
  7. data/lib/thor/actions/directory.rb +33 -23
  8. data/lib/thor/actions/empty_directory.rb +75 -85
  9. data/lib/thor/actions/file_manipulation.rb +103 -36
  10. data/lib/thor/actions/inject_into_file.rb +46 -36
  11. data/lib/thor/actions.rb +90 -68
  12. data/lib/thor/base.rb +302 -244
  13. data/lib/thor/command.rb +142 -0
  14. data/lib/thor/core_ext/hash_with_indifferent_access.rb +52 -24
  15. data/lib/thor/error.rb +90 -10
  16. data/lib/thor/group.rb +70 -74
  17. data/lib/thor/invocation.rb +63 -55
  18. data/lib/thor/line_editor/basic.rb +37 -0
  19. data/lib/thor/line_editor/readline.rb +88 -0
  20. data/lib/thor/line_editor.rb +17 -0
  21. data/lib/thor/nested_context.rb +29 -0
  22. data/lib/thor/parser/argument.rb +24 -28
  23. data/lib/thor/parser/arguments.rb +110 -102
  24. data/lib/thor/parser/option.rb +53 -15
  25. data/lib/thor/parser/options.rb +174 -97
  26. data/lib/thor/parser.rb +4 -4
  27. data/lib/thor/rake_compat.rb +12 -11
  28. data/lib/thor/runner.rb +159 -155
  29. data/lib/thor/shell/basic.rb +216 -93
  30. data/lib/thor/shell/color.rb +53 -40
  31. data/lib/thor/shell/html.rb +61 -58
  32. data/lib/thor/shell.rb +29 -36
  33. data/lib/thor/util.rb +231 -213
  34. data/lib/thor/version.rb +1 -1
  35. data/lib/thor.rb +303 -166
  36. data/thor.gemspec +27 -24
  37. metadata +36 -226
  38. data/.gitignore +0 -44
  39. data/.rspec +0 -2
  40. data/.travis.yml +0 -7
  41. data/CHANGELOG.rdoc +0 -134
  42. data/Gemfile +0 -15
  43. data/Thorfile +0 -30
  44. data/bin/rake2thor +0 -86
  45. data/lib/thor/core_ext/dir_escape.rb +0 -0
  46. data/lib/thor/core_ext/file_binary_read.rb +0 -9
  47. data/lib/thor/core_ext/ordered_hash.rb +0 -100
  48. data/lib/thor/task.rb +0 -132
  49. data/spec/actions/create_file_spec.rb +0 -170
  50. data/spec/actions/create_link_spec.rb +0 -81
  51. data/spec/actions/directory_spec.rb +0 -149
  52. data/spec/actions/empty_directory_spec.rb +0 -130
  53. data/spec/actions/file_manipulation_spec.rb +0 -370
  54. data/spec/actions/inject_into_file_spec.rb +0 -135
  55. data/spec/actions_spec.rb +0 -331
  56. data/spec/base_spec.rb +0 -279
  57. data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
  58. data/spec/core_ext/ordered_hash_spec.rb +0 -115
  59. data/spec/exit_condition_spec.rb +0 -19
  60. data/spec/fixtures/application.rb +0 -2
  61. data/spec/fixtures/app{1}/README +0 -3
  62. data/spec/fixtures/bundle/execute.rb +0 -6
  63. data/spec/fixtures/bundle/main.thor +0 -1
  64. data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
  65. data/spec/fixtures/doc/COMMENTER +0 -10
  66. data/spec/fixtures/doc/README +0 -3
  67. data/spec/fixtures/doc/block_helper.rb +0 -3
  68. data/spec/fixtures/doc/components/.empty_directory +0 -0
  69. data/spec/fixtures/doc/config.rb +0 -1
  70. data/spec/fixtures/doc/config.yaml.tt +0 -1
  71. data/spec/fixtures/enum.thor +0 -10
  72. data/spec/fixtures/group.thor +0 -114
  73. data/spec/fixtures/invoke.thor +0 -112
  74. data/spec/fixtures/path with spaces +0 -0
  75. data/spec/fixtures/script.thor +0 -190
  76. data/spec/fixtures/task.thor +0 -10
  77. data/spec/group_spec.rb +0 -216
  78. data/spec/invocation_spec.rb +0 -100
  79. data/spec/parser/argument_spec.rb +0 -53
  80. data/spec/parser/arguments_spec.rb +0 -66
  81. data/spec/parser/option_spec.rb +0 -202
  82. data/spec/parser/options_spec.rb +0 -330
  83. data/spec/rake_compat_spec.rb +0 -72
  84. data/spec/register_spec.rb +0 -135
  85. data/spec/runner_spec.rb +0 -241
  86. data/spec/shell/basic_spec.rb +0 -300
  87. data/spec/shell/color_spec.rb +0 -81
  88. data/spec/shell/html_spec.rb +0 -32
  89. data/spec/shell_spec.rb +0 -47
  90. data/spec/spec_helper.rb +0 -59
  91. data/spec/task_spec.rb +0 -80
  92. data/spec/thor_spec.rb +0 -418
  93. data/spec/util_spec.rb +0 -196
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 30e79d2b0a96e87c8e6348467db577c6ad1e9acbbbac5d375417bc3e5a2b7698
4
+ data.tar.gz: 701f1ab842da90e599b96bd00d90481d716eb29e39e0283b5ff527c2033fc742
5
+ SHA512:
6
+ metadata.gz: 73b1ac80575d4422204cd8072950b5594739db3b6f3fde0f2f04359d51b1d4428524d25b9a3003ae9ec3f6be615cf635f3057bbc65558e6a17ba490ff045988b
7
+ data.tar.gz: eb7761a5e6f3674cb3231398145978b0eb53a6fa2c10e4cb9e99d8d523988efcefc8cae5dd163b8a3d6ead7424422d58b92311c200790ad6e2416e3f9757a90e
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,15 @@
1
+ Pull Requests
2
+ -------------
3
+ Here are some reasons why a pull request may not be merged:
4
+
5
+ 1. It hasn’t been reviewed.
6
+ 2. It doesn’t include specs for new functionality.
7
+ 3. It doesn’t include documentation for new functionality.
8
+ 4. It changes behavior without changing the relevant documentation, comments, or specs.
9
+ 5. It changes behavior of an existing public API, breaking backward compatibility.
10
+ 6. It breaks the tests on a supported platform.
11
+ 7. It doesn’t merge cleanly (requiring Git rebasing and conflict resolution).
12
+
13
+ If you would like to help in this process, you can start by evaluating open pull requests against the criteria above. For example, if a pull request does not include specs for new functionality, you can add a comment like: “If you would like this feature to be added to Thor, please add specs to ensure that it does not break in the future.” This will help move a pull request closer to being merged.
14
+
15
+ Include this emoji in the top of your ticket to signal to us that you read this file: 🌈
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
- [![Build Status](https://secure.travis-ci.org/wycats/thor.png?branch=master)](http://travis-ci.org/wycats/thor)
2
-
3
1
  Thor
4
2
  ====
5
3
 
4
+ [![Gem Version](http://img.shields.io/gem/v/thor.svg)][gem]
5
+
6
+ [gem]: https://rubygems.org/gems/thor
7
+
6
8
  Description
7
9
  -----------
8
10
  Thor is a simple and efficient tool for building self-documenting command line
@@ -11,7 +13,13 @@ utilities. It removes the pain of parsing command line options, writing
11
13
  build tool. The syntax is Rake-like, so it should be familiar to most Rake
12
14
  users.
13
15
 
14
- [rake]: https://github.com/jimweirich/rake
16
+ Please note: Thor, by design, is a system tool created to allow seamless file and url
17
+ access, which should not receive application user input. It relies on [open-uri][open-uri],
18
+ which combined with application user input would provide a command injection attack
19
+ vector.
20
+
21
+ [rake]: https://github.com/ruby/rake
22
+ [open-uri]: https://ruby-doc.org/stdlib-2.5.1/libdoc/open-uri/rdoc/index.html
15
23
 
16
24
  Installation
17
25
  ------------
@@ -19,10 +27,19 @@ Installation
19
27
 
20
28
  Usage and documentation
21
29
  -----------------------
22
- Please see [the wiki](https://github.com/wycats/thor/wiki) for basic usage and other documentation on using Thor.
30
+ Please see the [wiki][] for basic usage and other documentation on using Thor. You can also checkout the [official homepage][homepage].
31
+
32
+ [wiki]: https://github.com/rails/thor/wiki
33
+ [homepage]: http://whatisthor.com/
34
+
35
+ Contributing
36
+ ------------
37
+ If you would like to help, please read the [CONTRIBUTING][] file for suggestions.
38
+
39
+ [contributing]: CONTRIBUTING.md
23
40
 
24
41
  License
25
42
  -------
26
- Released under the MIT License. See the [LICENSE][license] file for further details.
43
+ Released under the MIT License. See the [LICENSE][] file for further details.
27
44
 
28
- [license]: https://github.com/wycats/thor/blob/master/LICENSE.md
45
+ [license]: LICENSE.md
data/bin/thor CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- mode: ruby -*-
3
3
 
4
- require 'thor/runner'
4
+ require "thor/runner"
5
5
  $thor_runner = true
6
6
  Thor::Runner.start
@@ -1,8 +1,7 @@
1
- require 'thor/actions/empty_directory'
1
+ require_relative "empty_directory"
2
2
 
3
3
  class Thor
4
4
  module Actions
5
-
6
5
  # Create a new file relative to the destination root with the given data,
7
6
  # which is the return value of a block or a data string.
8
7
  #
@@ -25,7 +24,7 @@ class Thor
25
24
  data = args.first
26
25
  action CreateFile.new(self, destination, block || data.to_s, config)
27
26
  end
28
- alias :add_file :create_file
27
+ alias_method :add_file, :create_file
29
28
 
30
29
  # CreateFile is a subset of Template, which instead of rendering a file with
31
30
  # ERB, it gets the content from the user.
@@ -33,7 +32,7 @@ class Thor
33
32
  class CreateFile < EmptyDirectory #:nodoc:
34
33
  attr_reader :data
35
34
 
36
- def initialize(base, destination, data, config={})
35
+ def initialize(base, destination, data, config = {})
37
36
  @data = data
38
37
  super(base, destination, config)
39
38
  end
@@ -59,47 +58,47 @@ class Thor
59
58
 
60
59
  def invoke!
61
60
  invoke_with_conflict_check do
61
+ require "fileutils"
62
62
  FileUtils.mkdir_p(File.dirname(destination))
63
- File.open(destination, 'wb') { |f| f.write render }
63
+ File.open(destination, "wb") { |f| f.write render }
64
64
  end
65
65
  given_destination
66
66
  end
67
67
 
68
- protected
69
-
70
- # Now on conflict we check if the file is identical or not.
71
- #
72
- def on_conflict_behavior(&block)
73
- if identical?
74
- say_status :identical, :blue
75
- else
76
- options = base.options.merge(config)
77
- force_or_skip_or_conflict(options[:force], options[:skip], &block)
78
- end
79
- end
68
+ protected
80
69
 
81
- # If force is true, run the action, otherwise check if it's not being
82
- # skipped. If both are false, show the file_collision menu, if the menu
83
- # returns true, force it, otherwise skip.
84
- #
85
- def force_or_skip_or_conflict(force, skip, &block)
86
- if force
87
- say_status :force, :yellow
88
- block.call unless pretend?
89
- elsif skip
90
- say_status :skip, :yellow
91
- else
92
- say_status :conflict, :red
93
- force_or_skip_or_conflict(force_on_collision?, true, &block)
94
- end
70
+ # Now on conflict we check if the file is identical or not.
71
+ #
72
+ def on_conflict_behavior(&block)
73
+ if identical?
74
+ say_status :identical, :blue
75
+ else
76
+ options = base.options.merge(config)
77
+ force_or_skip_or_conflict(options[:force], options[:skip], &block)
95
78
  end
79
+ end
96
80
 
97
- # Shows the file collision menu to the user and gets the result.
98
- #
99
- def force_on_collision?
100
- base.shell.file_collision(destination){ render }
81
+ # If force is true, run the action, otherwise check if it's not being
82
+ # skipped. If both are false, show the file_collision menu, if the menu
83
+ # returns true, force it, otherwise skip.
84
+ #
85
+ def force_or_skip_or_conflict(force, skip, &block)
86
+ if force
87
+ say_status :force, :yellow
88
+ yield unless pretend?
89
+ elsif skip
90
+ say_status :skip, :yellow
91
+ else
92
+ say_status :conflict, :red
93
+ force_or_skip_or_conflict(force_on_collision?, true, &block)
101
94
  end
95
+ end
102
96
 
97
+ # Shows the file collision menu to the user and gets the result.
98
+ #
99
+ def force_on_collision?
100
+ base.shell.file_collision(destination) { render }
101
+ end
103
102
  end
104
103
  end
105
104
  end
@@ -1,8 +1,7 @@
1
- require 'thor/actions/create_file'
1
+ require_relative "create_file"
2
2
 
3
3
  class Thor
4
4
  module Actions
5
-
6
5
  # Create a new file relative to the destination root from the given source.
7
6
  #
8
7
  # ==== Parameters
@@ -15,12 +14,12 @@ class Thor
15
14
  #
16
15
  # create_link "config/apache.conf", "/etc/apache.conf"
17
16
  #
18
- def create_link(destination, *args, &block)
17
+ def create_link(destination, *args)
19
18
  config = args.last.is_a?(Hash) ? args.pop : {}
20
19
  source = args.first
21
20
  action CreateLink.new(self, destination, source, config)
22
21
  end
23
- alias :add_link :create_link
22
+ alias_method :add_link, :create_link
24
23
 
25
24
  # CreateLink is a subset of CreateFile, which instead of taking a block of
26
25
  # data, just takes a source string from the user.
@@ -34,11 +33,13 @@ class Thor
34
33
  # Boolean:: true if it is identical, false otherwise.
35
34
  #
36
35
  def identical?
37
- exists? && File.identical?(render, destination)
36
+ source = File.expand_path(render, File.dirname(destination))
37
+ exists? && File.identical?(source, destination)
38
38
  end
39
39
 
40
40
  def invoke!
41
41
  invoke_with_conflict_check do
42
+ require "fileutils"
42
43
  FileUtils.mkdir_p(File.dirname(destination))
43
44
  # Create a symlink by default
44
45
  config[:symbolic] = true if config[:symbolic].nil?
@@ -52,6 +53,9 @@ class Thor
52
53
  given_destination
53
54
  end
54
55
 
56
+ def exists?
57
+ super || File.symlink?(destination)
58
+ end
55
59
  end
56
60
  end
57
61
  end
@@ -1,4 +1,4 @@
1
- require 'thor/actions/empty_directory'
1
+ require_relative "empty_directory"
2
2
 
3
3
  class Thor
4
4
  module Actions
@@ -38,6 +38,8 @@ class Thor
38
38
  # destination<String>:: the relative path to the destination root.
39
39
  # config<Hash>:: give :verbose => false to not log the status.
40
40
  # If :recursive => false, does not look for paths recursively.
41
+ # If :mode => :preserve, preserve the file mode from the source.
42
+ # If :exclude_pattern => /regexp/, prevents copying files that match that regexp.
41
43
  #
42
44
  # ==== Examples
43
45
  #
@@ -53,10 +55,10 @@ class Thor
53
55
  class Directory < EmptyDirectory #:nodoc:
54
56
  attr_reader :source
55
57
 
56
- def initialize(base, source, destination=nil, config={}, &block)
57
- @source = File.expand_path(base.find_in_source_paths(source.to_s))
58
+ def initialize(base, source, destination = nil, config = {}, &block)
59
+ @source = File.expand_path(Dir[Util.escape_globs(base.find_in_source_paths(source.to_s))].first)
58
60
  @block = block
59
- super(base, destination, { :recursive => true }.merge(config))
61
+ super(base, destination, {:recursive => true}.merge(config))
60
62
  end
61
63
 
62
64
  def invoke!
@@ -68,31 +70,39 @@ class Thor
68
70
  execute!
69
71
  end
70
72
 
71
- protected
73
+ protected
72
74
 
73
- def execute!
74
- lookup = Util.escape_globs(source)
75
- lookup = config[:recursive] ? File.join(lookup, '**') : lookup
76
- lookup = File.join(lookup, '{*,.[a-z]*}')
75
+ def execute!
76
+ lookup = Util.escape_globs(source)
77
+ lookup = config[:recursive] ? File.join(lookup, "**") : lookup
78
+ lookup = file_level_lookup(lookup)
77
79
 
78
- Dir[lookup].sort.each do |file_source|
79
- next if File.directory?(file_source)
80
- file_destination = File.join(given_destination, file_source.gsub(source, '.'))
81
- file_destination.gsub!('/./', '/')
80
+ files(lookup).sort.each do |file_source|
81
+ next if File.directory?(file_source)
82
+ next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern])
83
+ file_destination = File.join(given_destination, file_source.gsub(source, "."))
84
+ file_destination.gsub!("/./", "/")
82
85
 
83
- case file_source
84
- when /\.empty_directory$/
85
- dirname = File.dirname(file_destination).gsub(/\/\.$/, '')
86
- next if dirname == given_destination
87
- base.empty_directory(dirname, config)
88
- when /\.tt$/
89
- destination = base.template(file_source, file_destination[0..-4], config, &@block)
90
- else
91
- destination = base.copy_file(file_source, file_destination, config, &@block)
92
- end
86
+ case file_source
87
+ when /\.empty_directory$/
88
+ dirname = File.dirname(file_destination).gsub(%r{/\.$}, "")
89
+ next if dirname == given_destination
90
+ base.empty_directory(dirname, config)
91
+ when /#{TEMPLATE_EXTNAME}$/
92
+ base.template(file_source, file_destination[0..-4], config, &@block)
93
+ else
94
+ base.copy_file(file_source, file_destination, config, &@block)
93
95
  end
94
96
  end
97
+ end
98
+
99
+ def file_level_lookup(previous_lookup)
100
+ File.join(previous_lookup, "*")
101
+ end
95
102
 
103
+ def files(lookup)
104
+ Dir.glob(lookup, File::FNM_DOTMATCH)
105
+ end
96
106
  end
97
107
  end
98
108
  end
@@ -1,6 +1,5 @@
1
1
  class Thor
2
2
  module Actions
3
-
4
3
  # Creates an empty directory.
5
4
  #
6
5
  # ==== Parameters
@@ -11,7 +10,7 @@ class Thor
11
10
  #
12
11
  # empty_directory "doc"
13
12
  #
14
- def empty_directory(destination, config={})
13
+ def empty_directory(destination, config = {})
15
14
  action EmptyDirectory.new(self, destination, config)
16
15
  end
17
16
 
@@ -32,8 +31,9 @@ class Thor
32
31
  # destination<String>:: Relative path to the destination of this file
33
32
  # config<Hash>:: give :verbose => false to not log the status.
34
33
  #
35
- def initialize(base, destination, config={})
36
- @base, @config = base, { :verbose => true }.merge(config)
34
+ def initialize(base, destination, config = {})
35
+ @base = base
36
+ @config = {:verbose => true}.merge(config)
37
37
  self.destination = destination
38
38
  end
39
39
 
@@ -43,111 +43,101 @@ class Thor
43
43
  # Boolean:: true if the file exists, false otherwise.
44
44
  #
45
45
  def exists?
46
- ::File.exists?(destination)
46
+ ::File.exist?(destination)
47
47
  end
48
48
 
49
49
  def invoke!
50
50
  invoke_with_conflict_check do
51
+ require "fileutils"
51
52
  ::FileUtils.mkdir_p(destination)
52
53
  end
53
54
  end
54
55
 
55
56
  def revoke!
56
57
  say_status :remove, :red
58
+ require "fileutils"
57
59
  ::FileUtils.rm_rf(destination) if !pretend? && exists?
58
60
  given_destination
59
61
  end
60
62
 
61
- protected
63
+ protected
62
64
 
63
- # Shortcut for pretend.
64
- #
65
- def pretend?
66
- base.options[:pretend]
67
- end
65
+ # Shortcut for pretend.
66
+ #
67
+ def pretend?
68
+ base.options[:pretend]
69
+ end
68
70
 
69
- # Sets the absolute destination value from a relative destination value.
70
- # It also stores the given and relative destination. Let's suppose our
71
- # script is being executed on "dest", it sets the destination root to
72
- # "dest". The destination, given_destination and relative_destination
73
- # are related in the following way:
74
- #
75
- # inside "bar" do
76
- # empty_directory "baz"
77
- # end
78
- #
79
- # destination #=> dest/bar/baz
80
- # relative_destination #=> bar/baz
81
- # given_destination #=> baz
82
- #
83
- def destination=(destination)
84
- if destination
85
- @given_destination = convert_encoded_instructions(destination.to_s)
86
- @destination = ::File.expand_path(@given_destination, base.destination_root)
87
- @relative_destination = base.relative_to_original_destination_root(@destination)
88
- end
89
- end
71
+ # Sets the absolute destination value from a relative destination value.
72
+ # It also stores the given and relative destination. Let's suppose our
73
+ # script is being executed on "dest", it sets the destination root to
74
+ # "dest". The destination, given_destination and relative_destination
75
+ # are related in the following way:
76
+ #
77
+ # inside "bar" do
78
+ # empty_directory "baz"
79
+ # end
80
+ #
81
+ # destination #=> dest/bar/baz
82
+ # relative_destination #=> bar/baz
83
+ # given_destination #=> baz
84
+ #
85
+ def destination=(destination)
86
+ return unless destination
87
+ @given_destination = convert_encoded_instructions(destination.to_s)
88
+ @destination = ::File.expand_path(@given_destination, base.destination_root)
89
+ @relative_destination = base.relative_to_original_destination_root(@destination)
90
+ end
90
91
 
91
- # Filenames in the encoded form are converted. If you have a file:
92
- #
93
- # %file_name%.rb
94
- #
95
- # It calls #file_name from the base and replaces %-string with the
96
- # return value (should be String) of #file_name:
97
- #
98
- # user.rb
99
- #
100
- # The method referenced by %-string SHOULD be public. Otherwise you
101
- # get the exception with the corresponding error message.
102
- #
103
- def convert_encoded_instructions(filename)
104
- filename.gsub(/%(.*?)%/) do |initial_string|
105
- call_public_method($1.strip) or initial_string
106
- end
92
+ # Filenames in the encoded form are converted. If you have a file:
93
+ #
94
+ # %file_name%.rb
95
+ #
96
+ # It calls #file_name from the base and replaces %-string with the
97
+ # return value (should be String) of #file_name:
98
+ #
99
+ # user.rb
100
+ #
101
+ # The method referenced can be either public or private.
102
+ #
103
+ def convert_encoded_instructions(filename)
104
+ filename.gsub(/%(.*?)%/) do |initial_string|
105
+ method = $1.strip
106
+ base.respond_to?(method, true) ? base.send(method) : initial_string
107
107
  end
108
+ end
108
109
 
109
- # Calls `base`'s public method `sym`.
110
- # Returns:: result of `base.sym` or `nil` if `sym` wasn't found in
111
- # `base`
112
- # Raises:: Thor::PrivateMethodEncodedError if `sym` references
113
- # a private method.
114
- def call_public_method(sym)
115
- if base.respond_to?(sym)
116
- base.send(sym)
117
- elsif base.respond_to?(sym, true)
118
- raise Thor::PrivateMethodEncodedError,
119
- "Method #{base.class}##{sym} should be public, not private"
120
- else
121
- nil
122
- end
110
+ # Receives a hash of options and just execute the block if some
111
+ # conditions are met.
112
+ #
113
+ def invoke_with_conflict_check(&block)
114
+ if exists?
115
+ on_conflict_behavior(&block)
116
+ else
117
+ yield unless pretend?
118
+ say_status :create, :green
123
119
  end
124
120
 
125
- # Receives a hash of options and just execute the block if some
126
- # conditions are met.
127
- #
128
- def invoke_with_conflict_check(&block)
129
- if exists?
130
- on_conflict_behavior(&block)
131
- else
132
- say_status :create, :green
133
- block.call unless pretend?
134
- end
135
-
136
- destination
137
- end
121
+ destination
122
+ rescue Errno::EISDIR, Errno::EEXIST
123
+ on_file_clash_behavior
124
+ end
138
125
 
139
- # What to do when the destination file already exists.
140
- #
141
- def on_conflict_behavior(&block)
142
- say_status :exist, :blue
143
- end
126
+ def on_file_clash_behavior
127
+ say_status :file_clash, :red
128
+ end
144
129
 
145
- # Shortcut to say_status shell method.
146
- #
147
- def say_status(status, color)
148
- base.shell.say_status status, relative_destination, color if config[:verbose]
149
- end
130
+ # What to do when the destination file already exists.
131
+ #
132
+ def on_conflict_behavior
133
+ say_status :exist, :blue
134
+ end
150
135
 
136
+ # Shortcut to say_status shell method.
137
+ #
138
+ def say_status(status, color)
139
+ base.shell.say_status status, relative_destination, color if config[:verbose]
140
+ end
151
141
  end
152
142
  end
153
143
  end