thor 0.19.4 → 1.2.1

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.
@@ -1,16 +1,18 @@
1
1
  class Thor
2
2
  class Option < Argument #:nodoc:
3
- attr_reader :aliases, :group, :lazy_default, :hide
3
+ attr_reader :aliases, :group, :lazy_default, :hide, :repeatable
4
4
 
5
5
  VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
6
6
 
7
7
  def initialize(name, options = {})
8
+ @check_default_type = options[:check_default_type]
8
9
  options[:required] = false unless options.key?(:required)
10
+ @repeatable = options.fetch(:repeatable, false)
9
11
  super
10
- @lazy_default = options[:lazy_default]
11
- @group = options[:group].to_s.capitalize if options[:group]
12
- @aliases = Array(options[:aliases])
13
- @hide = options[:hide]
12
+ @lazy_default = options[:lazy_default]
13
+ @group = options[:group].to_s.capitalize if options[:group]
14
+ @aliases = Array(options[:aliases])
15
+ @hide = options[:hide]
14
16
  end
15
17
 
16
18
  # This parse quick options given as method_options. It makes several
@@ -80,12 +82,12 @@ class Thor
80
82
 
81
83
  def usage(padding = 0)
82
84
  sample = if banner && !banner.to_s.empty?
83
- "#{switch_name}=#{banner}"
85
+ "#{switch_name}=#{banner}".dup
84
86
  else
85
87
  switch_name
86
88
  end
87
89
 
88
- sample = "[#{sample}]" unless required?
90
+ sample = "[#{sample}]".dup unless required?
89
91
 
90
92
  if boolean?
91
93
  sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-")
@@ -127,8 +129,19 @@ class Thor
127
129
  @default.class.name.downcase.to_sym
128
130
  end
129
131
 
130
- # TODO: This should raise an ArgumentError in a future version of Thor
131
- warn "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type
132
+ expected_type = (@repeatable && @type != :hash) ? :array : @type
133
+
134
+ if default_type != expected_type
135
+ err = "Expected #{expected_type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})"
136
+
137
+ if @check_default_type
138
+ raise ArgumentError, err
139
+ elsif @check_default_type == nil
140
+ Thor.deprecation_warning "#{err}.\n" +
141
+ 'This will be rejected in the future unless you explicitly pass the options `check_default_type: false`' +
142
+ ' or call `allow_incompatible_default_type!` in your code'
143
+ end
144
+ end
132
145
  end
133
146
 
134
147
  def dasherized?
@@ -18,19 +18,20 @@ class Thor
18
18
  when Hash
19
19
  "--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}"
20
20
  when nil, false
21
- ""
21
+ nil
22
22
  else
23
23
  "--#{key} #{value.inspect}"
24
24
  end
25
- end.join(" ")
25
+ end.compact.join(" ")
26
26
  end
27
27
 
28
28
  # Takes a hash of Thor::Option and a hash with defaults.
29
29
  #
30
30
  # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
31
31
  # an unknown option or a regular argument.
32
- def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false)
32
+ def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false)
33
33
  @stop_on_unknown = stop_on_unknown
34
+ @disable_required_check = disable_required_check
34
35
  options = hash_options.values
35
36
  super(options)
36
37
 
@@ -43,6 +44,8 @@ class Thor
43
44
  @shorts = {}
44
45
  @switches = {}
45
46
  @extra = []
47
+ @stopped_parsing_after_extra_index = nil
48
+ @is_treated_as_value = false
46
49
 
47
50
  options.each do |option|
48
51
  @switches[option.switch_name] = option
@@ -65,14 +68,26 @@ class Thor
65
68
  if result == OPTS_END
66
69
  shift
67
70
  @parsing_options = false
71
+ @stopped_parsing_after_extra_index ||= @extra.size
68
72
  super
69
73
  else
70
74
  result
71
75
  end
72
76
  end
73
77
 
78
+ def shift
79
+ @is_treated_as_value = false
80
+ super
81
+ end
82
+
83
+ def unshift(arg, is_value: false)
84
+ @is_treated_as_value = is_value
85
+ super(arg)
86
+ end
87
+
74
88
  def parse(args) # rubocop:disable MethodLength
75
89
  @pile = args.dup
90
+ @is_treated_as_value = false
76
91
  @parsing_options = true
77
92
 
78
93
  while peek
@@ -85,7 +100,10 @@ class Thor
85
100
  when SHORT_SQ_RE
86
101
  unshift($1.split("").map { |f| "-#{f}" })
87
102
  next
88
- when EQ_RE, SHORT_NUM
103
+ when EQ_RE
104
+ unshift($2, is_value: true)
105
+ switch = $1
106
+ when SHORT_NUM
89
107
  unshift($2)
90
108
  switch = $1
91
109
  when LONG_RE, SHORT_RE
@@ -94,10 +112,12 @@ class Thor
94
112
 
95
113
  switch = normalize_switch(switch)
96
114
  option = switch_option(switch)
97
- @assigns[option.human_name] = parse_peek(switch, option)
115
+ result = parse_peek(switch, option)
116
+ assign_result!(option, result)
98
117
  elsif @stop_on_unknown
99
118
  @parsing_options = false
100
119
  @extra << shifted
120
+ @stopped_parsing_after_extra_index ||= @extra.size
101
121
  @extra << shift while peek
102
122
  break
103
123
  elsif match
@@ -111,7 +131,7 @@ class Thor
111
131
  end
112
132
  end
113
133
 
114
- check_requirement!
134
+ check_requirement! unless @disable_required_check
115
135
 
116
136
  assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
117
137
  assigns.freeze
@@ -119,18 +139,31 @@ class Thor
119
139
  end
120
140
 
121
141
  def check_unknown!
142
+ to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra
143
+
122
144
  # an unknown option starts with - or -- and has no more --'s afterward.
123
- unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ }
124
- raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
145
+ unknown = to_check.select { |str| str =~ /^--?(?:(?!--).)*$/ }
146
+ raise UnknownArgumentError.new(@switches.keys, unknown) unless unknown.empty?
125
147
  end
126
148
 
127
149
  protected
128
150
 
151
+ def assign_result!(option, result)
152
+ if option.repeatable && option.type == :hash
153
+ (@assigns[option.human_name] ||= {}).merge!(result)
154
+ elsif option.repeatable
155
+ (@assigns[option.human_name] ||= []) << result
156
+ else
157
+ @assigns[option.human_name] = result
158
+ end
159
+ end
160
+
129
161
  # Check if the current value in peek is a registered switch.
130
162
  #
131
163
  # Two booleans are returned. The first is true if the current value
132
164
  # starts with a hyphen; the second is true if it is a registered switch.
133
165
  def current_is_switch?
166
+ return [false, false] if @is_treated_as_value
134
167
  case peek
135
168
  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
136
169
  [true, switch?($1)]
@@ -142,6 +175,7 @@ class Thor
142
175
  end
143
176
 
144
177
  def current_is_switch_formatted?
178
+ return false if @is_treated_as_value
145
179
  case peek
146
180
  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
147
181
  true
@@ -151,11 +185,12 @@ class Thor
151
185
  end
152
186
 
153
187
  def current_is_value?
188
+ return true if @is_treated_as_value
154
189
  peek && (!parsing_options? || super)
155
190
  end
156
191
 
157
192
  def switch?(arg)
158
- switch_option(normalize_switch(arg))
193
+ !switch_option(normalize_switch(arg)).nil?
159
194
  end
160
195
 
161
196
  def switch_option(arg)
@@ -188,7 +223,7 @@ class Thor
188
223
  shift
189
224
  false
190
225
  else
191
- true
226
+ @switches.key?(switch) || !no_or_skip?(switch)
192
227
  end
193
228
  else
194
229
  @switches.key?(switch) || !no_or_skip?(switch)
data/lib/thor/parser.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "thor/parser/argument"
2
- require "thor/parser/arguments"
3
- require "thor/parser/option"
4
- require "thor/parser/options"
1
+ require_relative "parser/argument"
2
+ require_relative "parser/arguments"
3
+ require_relative "parser/option"
4
+ require_relative "parser/options"
@@ -25,6 +25,7 @@ class Thor
25
25
  end
26
26
 
27
27
  def self.included(base)
28
+ super(base)
28
29
  # Hack. Make rakefile point to invoker, so rdoc task is generated properly.
29
30
  rakefile = File.basename(caller[0].match(/(.*):\d+/)[1])
30
31
  Rake.application.instance_variable_set(:@rakefile, rakefile)
data/lib/thor/runner.rb CHANGED
@@ -1,14 +1,13 @@
1
- require "thor"
2
- require "thor/group"
3
- require "thor/core_ext/io_binary_read"
1
+ require_relative "../thor"
2
+ require_relative "group"
4
3
 
5
- require "fileutils"
6
- require "open-uri"
7
4
  require "yaml"
8
5
  require "digest/md5"
9
6
  require "pathname"
10
7
 
11
8
  class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
9
+ autoload :OpenURI, "open-uri"
10
+
12
11
  map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
13
12
 
14
13
  def self.banner(command, all = false, subcommand = false)
@@ -104,6 +103,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
104
103
  if package == :file
105
104
  File.open(destination, "w") { |f| f.puts contents }
106
105
  else
106
+ require "fileutils"
107
107
  FileUtils.cp_r(name, destination)
108
108
  end
109
109
 
@@ -112,7 +112,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
112
112
 
113
113
  desc "version", "Show Thor version"
114
114
  def version
115
- require "thor/version"
115
+ require_relative "version"
116
116
  say "Thor #{Thor::VERSION}"
117
117
  end
118
118
 
@@ -120,6 +120,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
120
120
  def uninstall(name)
121
121
  raise Error, "Can't find module '#{name}'" unless thor_yaml[name]
122
122
  say "Uninstalling #{name}."
123
+ require "fileutils"
123
124
  FileUtils.rm_rf(File.join(thor_root, (thor_yaml[name][:filename]).to_s))
124
125
 
125
126
  thor_yaml.delete(name)
@@ -138,6 +139,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
138
139
  self.options = options.merge("as" => name)
139
140
 
140
141
  if File.directory? File.expand_path(name)
142
+ require "fileutils"
141
143
  FileUtils.rm_rf(File.join(thor_root, old_filename))
142
144
 
143
145
  thor_yaml.delete(old_filename)
@@ -194,6 +196,7 @@ private
194
196
  yaml_file = File.join(thor_root, "thor.yml")
195
197
 
196
198
  unless File.exist?(yaml_file)
199
+ require "fileutils"
197
200
  FileUtils.mkdir_p(thor_root)
198
201
  yaml_file = File.join(thor_root, "thor.yml")
199
202
  FileUtils.touch(yaml_file)
@@ -1,9 +1,8 @@
1
- require "tempfile"
2
- require "io/console" if RUBY_VERSION > "1.9.2"
3
-
4
1
  class Thor
5
2
  module Shell
6
3
  class Basic
4
+ DEFAULT_TERMINAL_WIDTH = 80
5
+
7
6
  attr_accessor :base
8
7
  attr_reader :padding
9
8
 
@@ -48,6 +47,10 @@ class Thor
48
47
 
49
48
  # Asks something to the user and receives a response.
50
49
  #
50
+ # If a default value is specified it will be presented to the user
51
+ # and allows them to select that value with an empty response. This
52
+ # option is ignored when limited answers are supplied.
53
+ #
51
54
  # If asked to limit the correct responses, you can pass in an
52
55
  # array of acceptable answers. If one of those is not supplied,
53
56
  # they will be shown a message stating that one of those answers
@@ -64,6 +67,8 @@ class Thor
64
67
  # ==== Example
65
68
  # ask("What is your name?")
66
69
  #
70
+ # ask("What is the planet furthest from the sun?", :default => "Pluto")
71
+ #
67
72
  # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
68
73
  #
69
74
  # ask("What is your password?", :echo => false)
@@ -89,6 +94,8 @@ class Thor
89
94
  # say("I know you knew that.")
90
95
  #
91
96
  def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
97
+ return if quiet?
98
+
92
99
  buffer = prepare_message(message, *color)
93
100
  buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
94
101
 
@@ -96,6 +103,23 @@ class Thor
96
103
  stdout.flush
97
104
  end
98
105
 
106
+ # Say (print) an error to the user. If the sentence ends with a whitespace
107
+ # or tab character, a new line is not appended (print + flush). Otherwise
108
+ # are passed straight to puts (behavior got from Highline).
109
+ #
110
+ # ==== Example
111
+ # say_error("error: something went wrong")
112
+ #
113
+ def say_error(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
114
+ return if quiet?
115
+
116
+ buffer = prepare_message(message, *color)
117
+ buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
118
+
119
+ stderr.print(buffer)
120
+ stderr.flush
121
+ end
122
+
99
123
  # Say a status with the given color and appends the message. Since this
100
124
  # method is used frequently by actions, it allows nil or false to be given
101
125
  # in log_status, avoiding the message from being shown. If a Symbol is
@@ -104,13 +128,14 @@ class Thor
104
128
  def say_status(status, message, log_status = true)
105
129
  return if quiet? || log_status == false
106
130
  spaces = " " * (padding + 1)
107
- color = log_status.is_a?(Symbol) ? log_status : :green
108
-
109
131
  status = status.to_s.rjust(12)
132
+ margin = " " * status.length + spaces
133
+
134
+ color = log_status.is_a?(Symbol) ? log_status : :green
110
135
  status = set_color status, color, true if color
111
136
 
112
- buffer = "#{status}#{spaces}#{message}"
113
- buffer << "\n" unless buffer.end_with?("\n")
137
+ message = message.to_s.chomp.gsub(/(?<!\A)^/, margin)
138
+ buffer = "#{status}#{spaces}#{message}\n"
114
139
 
115
140
  stdout.print(buffer)
116
141
  stdout.flush
@@ -165,7 +190,7 @@ class Thor
165
190
  colwidth = options[:colwidth]
166
191
  options[:truncate] = terminal_width if options[:truncate] == true
167
192
 
168
- formats << "%-#{colwidth + 2}s" if colwidth
193
+ formats << "%-#{colwidth + 2}s".dup if colwidth
169
194
  start = colwidth ? 1 : 0
170
195
 
171
196
  colcount = array.max { |a, b| a.size <=> b.size }.size
@@ -177,9 +202,9 @@ class Thor
177
202
  maximas << maxima
178
203
  formats << if index == colcount - 1
179
204
  # Don't output 2 trailing spaces when printing the last column
180
- "%-s"
205
+ "%-s".dup
181
206
  else
182
- "%-#{maxima + 2}s"
207
+ "%-#{maxima + 2}s".dup
183
208
  end
184
209
  end
185
210
 
@@ -187,7 +212,7 @@ class Thor
187
212
  formats << "%s"
188
213
 
189
214
  array.each do |row|
190
- sentence = ""
215
+ sentence = "".dup
191
216
 
192
217
  row.each_with_index do |column, index|
193
218
  maxima = maximas[index]
@@ -225,8 +250,21 @@ class Thor
225
250
  paras = message.split("\n\n")
226
251
 
227
252
  paras.map! do |unwrapped|
228
- unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
229
- end
253
+ words = unwrapped.split(" ")
254
+ counter = words.first.length
255
+ words.inject do |memo, word|
256
+ word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n")
257
+ counter = 0 if word.include? "\n"
258
+ if (counter + word.length + 1) < width
259
+ memo = "#{memo} #{word}"
260
+ counter += (word.length + 1)
261
+ else
262
+ memo = "#{memo}\n#{word}"
263
+ counter = word.length
264
+ end
265
+ memo
266
+ end
267
+ end.compact!
230
268
 
231
269
  paras.each do |para|
232
270
  para.split("\n").each do |line|
@@ -242,11 +280,11 @@ class Thor
242
280
  #
243
281
  # ==== Parameters
244
282
  # destination<String>:: the destination file to solve conflicts
245
- # block<Proc>:: an optional block that returns the value to be used in diff
283
+ # block<Proc>:: an optional block that returns the value to be used in diff and merge
246
284
  #
247
285
  def file_collision(destination)
248
286
  return true if @always_force
249
- options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
287
+ options = block_given? ? "[Ynaqdhm]" : "[Ynaqh]"
250
288
 
251
289
  loop do
252
290
  answer = ask(
@@ -255,6 +293,9 @@ class Thor
255
293
  )
256
294
 
257
295
  case answer
296
+ when nil
297
+ say ""
298
+ return true
258
299
  when is?(:yes), is?(:force), ""
259
300
  return true
260
301
  when is?(:no), is?(:skip)
@@ -267,6 +308,13 @@ class Thor
267
308
  when is?(:diff)
268
309
  show_diff(destination, yield) if block_given?
269
310
  say "Retrying..."
311
+ when is?(:merge)
312
+ if block_given? && !merge_tool.empty?
313
+ merge(destination, yield)
314
+ return nil
315
+ end
316
+
317
+ say "Please specify merge tool to `THOR_MERGE` env."
270
318
  else
271
319
  say file_collision_help
272
320
  end
@@ -279,11 +327,11 @@ class Thor
279
327
  result = if ENV["THOR_COLUMNS"]
280
328
  ENV["THOR_COLUMNS"].to_i
281
329
  else
282
- unix? ? dynamic_width : 80
330
+ unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
283
331
  end
284
- result < 10 ? 80 : result
332
+ result < 10 ? DEFAULT_TERMINAL_WIDTH : result
285
333
  rescue
286
- 80
334
+ DEFAULT_TERMINAL_WIDTH
287
335
  end
288
336
 
289
337
  # Called if something goes wrong during the execution. This is used by Thor
@@ -344,12 +392,14 @@ class Thor
344
392
  q - quit, abort
345
393
  d - diff, show the differences between the old and the new
346
394
  h - help, show this help
395
+ m - merge, run merge tool
347
396
  HELP
348
397
  end
349
398
 
350
399
  def show_diff(destination, content) #:nodoc:
351
400
  diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
352
401
 
402
+ require "tempfile"
353
403
  Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
354
404
  temp.write content
355
405
  temp.rewind
@@ -411,7 +461,7 @@ class Thor
411
461
 
412
462
  return unless result
413
463
 
414
- result.strip!
464
+ result = result.strip
415
465
 
416
466
  if default && result == ""
417
467
  default
@@ -422,15 +472,41 @@ class Thor
422
472
 
423
473
  def ask_filtered(statement, color, options)
424
474
  answer_set = options[:limited_to]
475
+ case_insensitive = options.fetch(:case_insensitive, false)
425
476
  correct_answer = nil
426
477
  until correct_answer
427
478
  answers = answer_set.join(", ")
428
479
  answer = ask_simply("#{statement} [#{answers}]", color, options)
429
- correct_answer = answer_set.include?(answer) ? answer : nil
480
+ correct_answer = answer_match(answer_set, answer, case_insensitive)
430
481
  say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
431
482
  end
432
483
  correct_answer
433
484
  end
485
+
486
+ def answer_match(possibilities, answer, case_insensitive)
487
+ if case_insensitive
488
+ possibilities.detect{ |possibility| possibility.downcase == answer.downcase }
489
+ else
490
+ possibilities.detect{ |possibility| possibility == answer }
491
+ end
492
+ end
493
+
494
+ def merge(destination, content) #:nodoc:
495
+ require "tempfile"
496
+ Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
497
+ temp.write content
498
+ temp.rewind
499
+ system %(#{merge_tool} "#{temp.path}" "#{destination}")
500
+ end
501
+ end
502
+
503
+ def merge_tool #:nodoc:
504
+ @merge_tool ||= ENV["THOR_MERGE"] || git_merge_tool
505
+ end
506
+
507
+ def git_merge_tool #:nodoc:
508
+ `git config merge.tool`.rstrip rescue ""
509
+ end
434
510
  end
435
511
  end
436
512
  end
@@ -1,4 +1,4 @@
1
- require "thor/shell/basic"
1
+ require_relative "basic"
2
2
 
3
3
  class Thor
4
4
  module Shell
@@ -97,7 +97,15 @@ class Thor
97
97
  protected
98
98
 
99
99
  def can_display_colors?
100
- stdout.tty?
100
+ are_colors_supported? && !are_colors_disabled?
101
+ end
102
+
103
+ def are_colors_supported?
104
+ stdout.tty? && ENV["TERM"] != "dumb"
105
+ end
106
+
107
+ def are_colors_disabled?
108
+ !ENV['NO_COLOR'].nil?
101
109
  end
102
110
 
103
111
  # Overwrite show_diff to show diff with colors if Diff::LCS is
@@ -1,4 +1,4 @@
1
- require "thor/shell/basic"
1
+ require_relative "basic"
2
2
 
3
3
  class Thor
4
4
  module Shell
@@ -51,13 +51,13 @@ class Thor
51
51
  def set_color(string, *colors)
52
52
  if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
53
53
  html_colors = colors.map { |color| lookup_color(color) }
54
- "<span style=\"#{html_colors.join('; ')};\">#{string}</span>"
54
+ "<span style=\"#{html_colors.join('; ')};\">#{Thor::Util.escape_html(string)}</span>"
55
55
  else
56
56
  color, bold = colors
57
57
  html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
58
58
  styles = [html_color]
59
59
  styles << BOLD if bold
60
- "<span style=\"#{styles.join('; ')};\">#{string}</span>"
60
+ "<span style=\"#{styles.join('; ')};\">#{Thor::Util.escape_html(string)}</span>"
61
61
  end
62
62
  end
63
63
 
data/lib/thor/shell.rb CHANGED
@@ -21,12 +21,12 @@ class Thor
21
21
  end
22
22
 
23
23
  module Shell
24
- SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width]
24
+ SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_error, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width]
25
25
  attr_writer :shell
26
26
 
27
- autoload :Basic, "thor/shell/basic"
28
- autoload :Color, "thor/shell/color"
29
- autoload :HTML, "thor/shell/html"
27
+ autoload :Basic, File.expand_path("shell/basic", __dir__)
28
+ autoload :Color, File.expand_path("shell/color", __dir__)
29
+ autoload :HTML, File.expand_path("shell/html", __dir__)
30
30
 
31
31
  # Add shell to initialize config values.
32
32
  #
@@ -55,7 +55,7 @@ class Thor
55
55
 
56
56
  # Common methods that are delegated to the shell.
57
57
  SHELL_DELEGATED_METHODS.each do |method|
58
- module_eval <<-METHOD, __FILE__, __LINE__
58
+ module_eval <<-METHOD, __FILE__, __LINE__ + 1
59
59
  def #{method}(*args,&block)
60
60
  shell.#{method}(*args,&block)
61
61
  end
data/lib/thor/util.rb CHANGED
@@ -27,7 +27,7 @@ class Thor
27
27
  end
28
28
 
29
29
  # Receives a constant and converts it to a Thor namespace. Since Thor
30
- # commands can be added to a sandbox, this method is also responsable for
30
+ # commands can be added to a sandbox, this method is also responsible for
31
31
  # removing the sandbox namespace.
32
32
  #
33
33
  # This method should not be used in general because it's used to deal with
@@ -211,7 +211,7 @@ class Thor
211
211
  #
212
212
  def globs_for(path)
213
213
  path = escape_globs(path)
214
- ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"]
214
+ ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/**/*.thor"]
215
215
  end
216
216
 
217
217
  # Return the path to the ruby interpreter taking into account multiple
@@ -263,6 +263,22 @@ class Thor
263
263
  def escape_globs(path)
264
264
  path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
265
265
  end
266
+
267
+ # Returns a string that has had any HTML characters escaped.
268
+ #
269
+ # ==== Examples
270
+ #
271
+ # Thor::Util.escape_html('<div>') # => "&lt;div&gt;"
272
+ #
273
+ # ==== Parameters
274
+ # String
275
+ #
276
+ # ==== Returns
277
+ # String
278
+ #
279
+ def escape_html(string)
280
+ CGI.escapeHTML(string)
281
+ end
266
282
  end
267
283
  end
268
284
  end
data/lib/thor/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Thor
2
- VERSION = "0.19.4"
2
+ VERSION = "1.2.1"
3
3
  end