thor 0.18.1 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +13 -7
  3. data/Thorfile +4 -5
  4. data/bin/thor +1 -1
  5. data/lib/thor.rb +78 -67
  6. data/lib/thor/actions.rb +57 -56
  7. data/lib/thor/actions/create_file.rb +33 -35
  8. data/lib/thor/actions/create_link.rb +2 -3
  9. data/lib/thor/actions/directory.rb +37 -38
  10. data/lib/thor/actions/empty_directory.rb +67 -69
  11. data/lib/thor/actions/file_manipulation.rb +17 -15
  12. data/lib/thor/actions/inject_into_file.rb +27 -29
  13. data/lib/thor/base.rb +193 -189
  14. data/lib/thor/command.rb +20 -23
  15. data/lib/thor/core_ext/hash_with_indifferent_access.rb +21 -24
  16. data/lib/thor/core_ext/io_binary_read.rb +2 -4
  17. data/lib/thor/core_ext/ordered_hash.rb +9 -11
  18. data/lib/thor/error.rb +5 -1
  19. data/lib/thor/group.rb +53 -54
  20. data/lib/thor/invocation.rb +44 -38
  21. data/lib/thor/line_editor.rb +17 -0
  22. data/lib/thor/line_editor/basic.rb +35 -0
  23. data/lib/thor/line_editor/readline.rb +88 -0
  24. data/lib/thor/parser.rb +4 -4
  25. data/lib/thor/parser/argument.rb +28 -29
  26. data/lib/thor/parser/arguments.rb +102 -98
  27. data/lib/thor/parser/option.rb +26 -22
  28. data/lib/thor/parser/options.rb +86 -86
  29. data/lib/thor/rake_compat.rb +9 -10
  30. data/lib/thor/runner.rb +141 -141
  31. data/lib/thor/shell.rb +27 -34
  32. data/lib/thor/shell/basic.rb +91 -63
  33. data/lib/thor/shell/color.rb +44 -43
  34. data/lib/thor/shell/html.rb +59 -60
  35. data/lib/thor/util.rb +24 -27
  36. data/lib/thor/version.rb +1 -1
  37. data/spec/actions/create_file_spec.rb +25 -27
  38. data/spec/actions/create_link_spec.rb +19 -18
  39. data/spec/actions/directory_spec.rb +31 -31
  40. data/spec/actions/empty_directory_spec.rb +18 -18
  41. data/spec/actions/file_manipulation_spec.rb +38 -28
  42. data/spec/actions/inject_into_file_spec.rb +13 -13
  43. data/spec/actions_spec.rb +43 -43
  44. data/spec/base_spec.rb +45 -38
  45. data/spec/command_spec.rb +13 -14
  46. data/spec/core_ext/hash_with_indifferent_access_spec.rb +19 -19
  47. data/spec/core_ext/ordered_hash_spec.rb +6 -6
  48. data/spec/exit_condition_spec.rb +4 -4
  49. data/spec/fixtures/invoke.thor +19 -0
  50. data/spec/fixtures/script.thor +1 -1
  51. data/spec/group_spec.rb +30 -24
  52. data/spec/helper.rb +28 -15
  53. data/spec/invocation_spec.rb +39 -19
  54. data/spec/line_editor/basic_spec.rb +28 -0
  55. data/spec/line_editor/readline_spec.rb +69 -0
  56. data/spec/line_editor_spec.rb +43 -0
  57. data/spec/parser/argument_spec.rb +12 -12
  58. data/spec/parser/arguments_spec.rb +11 -11
  59. data/spec/parser/option_spec.rb +33 -25
  60. data/spec/parser/options_spec.rb +66 -52
  61. data/spec/quality_spec.rb +75 -0
  62. data/spec/rake_compat_spec.rb +10 -10
  63. data/spec/register_spec.rb +60 -30
  64. data/spec/runner_spec.rb +67 -62
  65. data/spec/sandbox/application.rb +2 -0
  66. data/spec/sandbox/app{1}/README +3 -0
  67. data/spec/sandbox/bundle/execute.rb +6 -0
  68. data/spec/sandbox/bundle/main.thor +1 -0
  69. data/spec/sandbox/command.thor +10 -0
  70. data/spec/sandbox/doc/%file_name%.rb.tt +1 -0
  71. data/spec/sandbox/doc/COMMENTER +11 -0
  72. data/spec/sandbox/doc/README +3 -0
  73. data/spec/sandbox/doc/block_helper.rb +3 -0
  74. data/spec/sandbox/doc/config.rb +1 -0
  75. data/spec/sandbox/doc/config.yaml.tt +1 -0
  76. data/spec/sandbox/doc/excluding/%file_name%.rb.tt +1 -0
  77. data/spec/sandbox/enum.thor +10 -0
  78. data/spec/sandbox/group.thor +128 -0
  79. data/spec/sandbox/invoke.thor +131 -0
  80. data/spec/sandbox/path with spaces b/data/spec/sandbox/path with → spaces +0 -0
  81. data/spec/sandbox/preserve/script.sh +3 -0
  82. data/spec/sandbox/script.thor +220 -0
  83. data/spec/sandbox/subcommand.thor +17 -0
  84. data/spec/shell/basic_spec.rb +107 -86
  85. data/spec/shell/color_spec.rb +32 -8
  86. data/spec/shell/html_spec.rb +3 -4
  87. data/spec/shell_spec.rb +7 -7
  88. data/spec/subcommand_spec.rb +20 -2
  89. data/spec/thor_spec.rb +111 -97
  90. data/spec/util_spec.rb +30 -30
  91. data/thor.gemspec +14 -14
  92. metadata +69 -25
@@ -1,8 +1,7 @@
1
- require 'thor/actions/empty_directory'
1
+ require "thor/actions/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
@@ -60,46 +59,45 @@ class Thor
60
59
  def invoke!
61
60
  invoke_with_conflict_check do
62
61
  FileUtils.mkdir_p(File.dirname(destination))
63
- File.open(destination, 'wb') { |f| f.write render }
62
+ File.open(destination, "wb") { |f| f.write render }
64
63
  end
65
64
  given_destination
66
65
  end
67
66
 
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
67
+ protected
80
68
 
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
69
+ # Now on conflict we check if the file is identical or not.
70
+ #
71
+ def on_conflict_behavior(&block)
72
+ if identical?
73
+ say_status :identical, :blue
74
+ else
75
+ options = base.options.merge(config)
76
+ force_or_skip_or_conflict(options[:force], options[:skip], &block)
95
77
  end
78
+ end
96
79
 
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 }
80
+ # If force is true, run the action, otherwise check if it's not being
81
+ # skipped. If both are false, show the file_collision menu, if the menu
82
+ # returns true, force it, otherwise skip.
83
+ #
84
+ def force_or_skip_or_conflict(force, skip, &block)
85
+ if force
86
+ say_status :force, :yellow
87
+ block.call unless pretend?
88
+ elsif skip
89
+ say_status :skip, :yellow
90
+ else
91
+ say_status :conflict, :red
92
+ force_or_skip_or_conflict(force_on_collision?, true, &block)
101
93
  end
94
+ end
102
95
 
96
+ # Shows the file collision menu to the user and gets the result.
97
+ #
98
+ def force_on_collision?
99
+ base.shell.file_collision(destination) { render }
100
+ end
103
101
  end
104
102
  end
105
103
  end
@@ -1,8 +1,7 @@
1
- require 'thor/actions/create_file'
1
+ require "thor/actions/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
@@ -20,7 +19,7 @@ class Thor
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.
@@ -1,4 +1,4 @@
1
- require 'thor/actions/empty_directory'
1
+ require "thor/actions/empty_directory"
2
2
 
3
3
  class Thor
4
4
  module Actions
@@ -55,10 +55,10 @@ class Thor
55
55
  class Directory < EmptyDirectory #:nodoc:
56
56
  attr_reader :source
57
57
 
58
- def initialize(base, source, destination=nil, config={}, &block)
58
+ def initialize(base, source, destination = nil, config = {}, &block)
59
59
  @source = File.expand_path(base.find_in_source_paths(source.to_s))
60
60
  @block = block
61
- super(base, destination, { :recursive => true }.merge(config))
61
+ super(base, destination, {:recursive => true}.merge(config))
62
62
  end
63
63
 
64
64
  def invoke!
@@ -70,50 +70,49 @@ class Thor
70
70
  execute!
71
71
  end
72
72
 
73
- protected
73
+ protected
74
74
 
75
- def execute!
76
- lookup = Util.escape_globs(source)
77
- lookup = config[:recursive] ? File.join(lookup, '**') : lookup
78
- lookup = file_level_lookup(lookup)
75
+ def execute! # rubocop:disable MethodLength
76
+ lookup = Util.escape_globs(source)
77
+ lookup = config[:recursive] ? File.join(lookup, "**") : lookup
78
+ lookup = file_level_lookup(lookup)
79
79
 
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!('/./', '/')
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!("/./", "/")
85
85
 
86
- case file_source
87
- when /\.empty_directory$/
88
- dirname = File.dirname(file_destination).gsub(/\/\.$/, '')
89
- next if dirname == given_destination
90
- base.empty_directory(dirname, config)
91
- when /\.tt$/
92
- destination = base.template(file_source, file_destination[0..-4], config, &@block)
93
- else
94
- destination = base.copy_file(file_source, file_destination, config, &@block)
95
- end
86
+ case file_source
87
+ when /\.empty_directory$/
88
+ dirname = File.dirname(file_destination).gsub(/\/\.$/, "")
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)
96
95
  end
97
96
  end
97
+ end
98
98
 
99
- if RUBY_VERSION < '2.0'
100
- def file_level_lookup(previous_lookup)
101
- File.join(previous_lookup, '{*,.[a-z]*}')
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
+ if RUBY_VERSION < "2.0"
100
+ def file_level_lookup(previous_lookup)
101
+ File.join(previous_lookup, "{*,.[a-z]*}")
102
+ end
111
103
 
112
- def files(lookup)
113
- Dir.glob(lookup, File::FNM_DOTMATCH)
114
- end
104
+ def files(lookup)
105
+ Dir[lookup]
106
+ end
107
+ else
108
+ def file_level_lookup(previous_lookup)
109
+ File.join(previous_lookup, "*")
115
110
  end
116
111
 
112
+ def files(lookup)
113
+ Dir.glob(lookup, File::FNM_DOTMATCH)
114
+ end
115
+ end
117
116
  end
118
117
  end
119
118
  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,8 @@ 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, @config = base, {:verbose => true}.merge(config)
37
36
  self.destination = destination
38
37
  end
39
38
 
@@ -43,7 +42,7 @@ class Thor
43
42
  # Boolean:: true if the file exists, false otherwise.
44
43
  #
45
44
  def exists?
46
- ::File.exists?(destination)
45
+ ::File.exist?(destination)
47
46
  end
48
47
 
49
48
  def invoke!
@@ -58,80 +57,79 @@ class Thor
58
57
  given_destination
59
58
  end
60
59
 
61
- protected
60
+ protected
62
61
 
63
- # Shortcut for pretend.
64
- #
65
- def pretend?
66
- base.options[:pretend]
67
- end
62
+ # Shortcut for pretend.
63
+ #
64
+ def pretend?
65
+ base.options[:pretend]
66
+ end
68
67
 
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
68
+ # Sets the absolute destination value from a relative destination value.
69
+ # It also stores the given and relative destination. Let's suppose our
70
+ # script is being executed on "dest", it sets the destination root to
71
+ # "dest". The destination, given_destination and relative_destination
72
+ # are related in the following way:
73
+ #
74
+ # inside "bar" do
75
+ # empty_directory "baz"
76
+ # end
77
+ #
78
+ # destination #=> dest/bar/baz
79
+ # relative_destination #=> bar/baz
80
+ # given_destination #=> baz
81
+ #
82
+ def destination=(destination)
83
+ if destination
84
+ @given_destination = convert_encoded_instructions(destination.to_s)
85
+ @destination = ::File.expand_path(@given_destination, base.destination_root)
86
+ @relative_destination = base.relative_to_original_destination_root(@destination)
89
87
  end
88
+ end
90
89
 
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 can be either public or private.
101
- #
102
- def convert_encoded_instructions(filename)
103
- filename.gsub(/%(.*?)%/) do |initial_string|
104
- method = $1.strip
105
- base.respond_to?(method, true) ? base.send(method) : initial_string
106
- end
90
+ # Filenames in the encoded form are converted. If you have a file:
91
+ #
92
+ # %file_name%.rb
93
+ #
94
+ # It calls #file_name from the base and replaces %-string with the
95
+ # return value (should be String) of #file_name:
96
+ #
97
+ # user.rb
98
+ #
99
+ # The method referenced can be either public or private.
100
+ #
101
+ def convert_encoded_instructions(filename)
102
+ filename.gsub(/%(.*?)%/) do |initial_string|
103
+ method = $1.strip
104
+ base.respond_to?(method, true) ? base.send(method) : initial_string
107
105
  end
106
+ end
108
107
 
109
- # Receives a hash of options and just execute the block if some
110
- # conditions are met.
111
- #
112
- def invoke_with_conflict_check(&block)
113
- if exists?
114
- on_conflict_behavior(&block)
115
- else
116
- say_status :create, :green
117
- block.call unless pretend?
118
- end
119
-
120
- destination
108
+ # Receives a hash of options and just execute the block if some
109
+ # conditions are met.
110
+ #
111
+ def invoke_with_conflict_check(&block)
112
+ if exists?
113
+ on_conflict_behavior(&block)
114
+ else
115
+ say_status :create, :green
116
+ block.call unless pretend?
121
117
  end
122
118
 
123
- # What to do when the destination file already exists.
124
- #
125
- def on_conflict_behavior(&block)
126
- say_status :exist, :blue
127
- end
119
+ destination
120
+ end
128
121
 
129
- # Shortcut to say_status shell method.
130
- #
131
- def say_status(status, color)
132
- base.shell.say_status status, relative_destination, color if config[:verbose]
133
- end
122
+ # What to do when the destination file already exists.
123
+ #
124
+ def on_conflict_behavior(&block)
125
+ say_status :exist, :blue
126
+ end
134
127
 
128
+ # Shortcut to say_status shell method.
129
+ #
130
+ def say_status(status, color)
131
+ base.shell.say_status status, relative_destination, color if config[:verbose]
132
+ end
135
133
  end
136
134
  end
137
135
  end
@@ -1,9 +1,8 @@
1
- require 'erb'
2
- require 'open-uri'
1
+ require "erb"
2
+ require "open-uri"
3
3
 
4
4
  class Thor
5
5
  module Actions
6
-
7
6
  # Copies the file from the relative source to the relative destination. If
8
7
  # the destination is not given it's assumed to be equal to the source.
9
8
  #
@@ -79,8 +78,8 @@ class Thor
79
78
  config = args.last.is_a?(Hash) ? args.pop : {}
80
79
  destination = args.first
81
80
 
82
- source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^https?\:\/\//
83
- render = open(source) {|input| input.binmode.read }
81
+ source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ %r{^https?\://}
82
+ render = open(source) { |input| input.binmode.read }
84
83
 
85
84
  destination ||= if block_given?
86
85
  block.arity == 1 ? block.call(render) : block.call
@@ -108,13 +107,13 @@ class Thor
108
107
  #
109
108
  def template(source, *args, &block)
110
109
  config = args.last.is_a?(Hash) ? args.pop : {}
111
- destination = args.first || source.sub(/\.tt$/, '')
110
+ destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "")
112
111
 
113
112
  source = File.expand_path(find_in_source_paths(source.to_s))
114
- context = instance_eval('binding')
113
+ context = instance_eval("binding")
115
114
 
116
115
  create_file destination, nil, config do
117
- content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context)
116
+ content = ERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context)
118
117
  content = block.call(content) if block
119
118
  content
120
119
  end
@@ -131,7 +130,7 @@ class Thor
131
130
  #
132
131
  # chmod "script/server", 0755
133
132
  #
134
- def chmod(path, mode, config={})
133
+ def chmod(path, mode, config = {})
135
134
  return unless behavior == :invoke
136
135
  path = File.expand_path(path, destination_root)
137
136
  say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
@@ -231,7 +230,7 @@ class Thor
231
230
  unless options[:pretend]
232
231
  content = File.binread(path)
233
232
  content.gsub!(flag, *args, &block)
234
- File.open(path, 'wb') { |file| file.write(content) }
233
+ File.open(path, "wb") { |file| file.write(content) }
235
234
  end
236
235
  end
237
236
 
@@ -284,17 +283,20 @@ class Thor
284
283
  # remove_file 'README'
285
284
  # remove_file 'app/controllers/application_controller.rb'
286
285
  #
287
- def remove_file(path, config={})
286
+ def remove_file(path, config = {})
288
287
  return unless behavior == :invoke
289
288
  path = File.expand_path(path, destination_root)
290
289
 
291
290
  say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
292
- ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path)
291
+ ::FileUtils.rm_rf(path) if !options[:pretend] && File.exist?(path)
293
292
  end
294
- alias :remove_dir :remove_file
293
+ alias_method :remove_dir, :remove_file
295
294
 
296
- private
297
295
  attr_accessor :output_buffer
296
+ private :output_buffer, :output_buffer=
297
+
298
+ private
299
+
298
300
  def concat(string)
299
301
  @output_buffer.concat(string)
300
302
  end
@@ -303,7 +305,7 @@ class Thor
303
305
  with_output_buffer { block.call(*args) }
304
306
  end
305
307
 
306
- def with_output_buffer(buf = '') #:nodoc:
308
+ def with_output_buffer(buf = "") #:nodoc:
307
309
  self.output_buffer, old_buffer = buf, output_buffer
308
310
  yield
309
311
  output_buffer