thor 0.20.3 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -9
  3. data/lib/thor/actions/create_file.rb +4 -3
  4. data/lib/thor/actions/create_link.rb +3 -2
  5. data/lib/thor/actions/directory.rb +8 -18
  6. data/lib/thor/actions/empty_directory.rb +1 -1
  7. data/lib/thor/actions/file_manipulation.rb +22 -24
  8. data/lib/thor/actions/inject_into_file.rb +34 -13
  9. data/lib/thor/actions.rb +39 -30
  10. data/lib/thor/base.rb +196 -49
  11. data/lib/thor/command.rb +34 -18
  12. data/lib/thor/core_ext/hash_with_indifferent_access.rb +10 -0
  13. data/lib/thor/error.rb +14 -22
  14. data/lib/thor/group.rb +13 -2
  15. data/lib/thor/invocation.rb +2 -1
  16. data/lib/thor/line_editor/basic.rb +1 -1
  17. data/lib/thor/line_editor/readline.rb +6 -6
  18. data/lib/thor/line_editor.rb +2 -2
  19. data/lib/thor/nested_context.rb +29 -0
  20. data/lib/thor/parser/argument.rb +17 -1
  21. data/lib/thor/parser/arguments.rb +35 -15
  22. data/lib/thor/parser/option.rb +45 -13
  23. data/lib/thor/parser/options.rb +79 -11
  24. data/lib/thor/parser.rb +4 -4
  25. data/lib/thor/rake_compat.rb +3 -2
  26. data/lib/thor/runner.rb +43 -32
  27. data/lib/thor/shell/basic.rb +68 -162
  28. data/lib/thor/shell/color.rb +9 -43
  29. data/lib/thor/shell/column_printer.rb +29 -0
  30. data/lib/thor/shell/html.rb +7 -49
  31. data/lib/thor/shell/lcs_diff.rb +49 -0
  32. data/lib/thor/shell/table_printer.rb +118 -0
  33. data/lib/thor/shell/terminal.rb +42 -0
  34. data/lib/thor/shell/wrapped_printer.rb +38 -0
  35. data/lib/thor/shell.rb +5 -5
  36. data/lib/thor/util.rb +25 -8
  37. data/lib/thor/version.rb +1 -1
  38. data/lib/thor.rb +182 -17
  39. data/thor.gemspec +22 -10
  40. metadata +25 -11
  41. data/CHANGELOG.md +0 -204
  42. data/lib/thor/core_ext/io_binary_read.rb +0 -12
  43. data/lib/thor/core_ext/ordered_hash.rb +0 -129
@@ -0,0 +1,29 @@
1
+ class Thor
2
+ class NestedContext
3
+ def initialize
4
+ @depth = 0
5
+ end
6
+
7
+ def enter
8
+ push
9
+
10
+ yield
11
+ ensure
12
+ pop
13
+ end
14
+
15
+ def entered?
16
+ @depth.positive?
17
+ end
18
+
19
+ private
20
+
21
+ def push
22
+ @depth += 1
23
+ end
24
+
25
+ def pop
26
+ @depth -= 1
27
+ end
28
+ end
29
+ end
@@ -24,6 +24,14 @@ class Thor
24
24
  validate! # Trigger specific validations
25
25
  end
26
26
 
27
+ def print_default
28
+ if @type == :array and @default.is_a?(Array)
29
+ @default.map(&:dump).join(" ")
30
+ else
31
+ @default
32
+ end
33
+ end
34
+
27
35
  def usage
28
36
  required? ? banner : "[#{banner}]"
29
37
  end
@@ -41,11 +49,19 @@ class Thor
41
49
  end
42
50
  end
43
51
 
52
+ def enum_to_s
53
+ if enum.respond_to? :join
54
+ enum.join(", ")
55
+ else
56
+ "#{enum.first}..#{enum.last}"
57
+ end
58
+ end
59
+
44
60
  protected
45
61
 
46
62
  def validate!
47
63
  raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
48
- raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array)
64
+ raise ArgumentError, "An argument cannot have an enum other than an enumerable." if @enum && !@enum.is_a?(Enumerable)
49
65
  end
50
66
 
51
67
  def valid_type?(type)
@@ -1,5 +1,5 @@
1
1
  class Thor
2
- class Arguments #:nodoc: # rubocop:disable ClassLength
2
+ class Arguments #:nodoc:
3
3
  NUMERIC = /[-+]?(\d*\.\d+|\d+)/
4
4
 
5
5
  # Receives an array of args and returns two arrays, one with arguments
@@ -9,7 +9,7 @@ class Thor
9
9
  arguments = []
10
10
 
11
11
  args.each do |item|
12
- break if item =~ /^-/
12
+ break if item.is_a?(String) && item =~ /^-/
13
13
  arguments << item
14
14
  end
15
15
 
@@ -30,7 +30,7 @@ class Thor
30
30
 
31
31
  arguments.each do |argument|
32
32
  if !argument.default.nil?
33
- @assigns[argument.human_name] = argument.default
33
+ @assigns[argument.human_name] = argument.default.dup
34
34
  elsif argument.required?
35
35
  @non_assigned_required << argument
36
36
  end
@@ -82,7 +82,7 @@ class Thor
82
82
  end
83
83
 
84
84
  def current_is_value?
85
- peek && peek.to_s !~ /^-/
85
+ peek && peek.to_s !~ /^-{1,2}\S+/
86
86
  end
87
87
 
88
88
  # Runs through the argument array getting strings that contains ":" and
@@ -117,8 +117,18 @@ class Thor
117
117
  #
118
118
  def parse_array(name)
119
119
  return shift if peek.is_a?(Array)
120
+
120
121
  array = []
121
- array << shift while current_is_value?
122
+
123
+ while current_is_value?
124
+ value = shift
125
+
126
+ if !value.empty?
127
+ validate_enum_value!(name, value, "Expected all values of '%s' to be one of %s; got %s")
128
+ end
129
+
130
+ array << value
131
+ end
122
132
  array
123
133
  end
124
134
 
@@ -134,11 +144,9 @@ class Thor
134
144
  end
135
145
 
136
146
  value = $&.index(".") ? shift.to_f : shift.to_i
137
- if @switches.is_a?(Hash) && switch = @switches[name]
138
- if switch.enum && !switch.enum.include?(value)
139
- raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
140
- end
141
- end
147
+
148
+ validate_enum_value!(name, value, "Expected '%s' to be one of %s; got %s")
149
+
142
150
  value
143
151
  end
144
152
 
@@ -152,15 +160,27 @@ class Thor
152
160
  nil
153
161
  else
154
162
  value = shift
155
- if @switches.is_a?(Hash) && switch = @switches[name]
156
- if switch.enum && !switch.enum.include?(value)
157
- raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
158
- end
159
- end
163
+
164
+ validate_enum_value!(name, value, "Expected '%s' to be one of %s; got %s")
165
+
160
166
  value
161
167
  end
162
168
  end
163
169
 
170
+ # Raises an error if the switch is an enum and the values aren't included on it.
171
+ #
172
+ def validate_enum_value!(name, value, message)
173
+ return unless @switches.is_a?(Hash)
174
+
175
+ switch = @switches[name]
176
+
177
+ return unless switch
178
+
179
+ if switch.enum && !switch.enum.include?(value)
180
+ raise MalformattedArgumentError, message % [name, switch.enum_to_s, value]
181
+ end
182
+ end
183
+
164
184
  # Raises an error if @non_assigned_required array is not empty.
165
185
  #
166
186
  def check_requirement!
@@ -1,17 +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
8
  @check_default_type = options[:check_default_type]
9
9
  options[:required] = false unless options.key?(:required)
10
+ @repeatable = options.fetch(:repeatable, false)
10
11
  super
11
- @lazy_default = options[:lazy_default]
12
- @group = options[:group].to_s.capitalize if options[:group]
13
- @aliases = Array(options[:aliases])
14
- @hide = options[:hide]
12
+ @lazy_default = options[:lazy_default]
13
+ @group = options[:group].to_s.capitalize if options[:group]
14
+ @aliases = normalize_aliases(options[:aliases])
15
+ @hide = options[:hide]
15
16
  end
16
17
 
17
18
  # This parse quick options given as method_options. It makes several
@@ -57,7 +58,7 @@ class Thor
57
58
  default = nil
58
59
  if VALID_TYPES.include?(value)
59
60
  value
60
- elsif required = (value == :required) # rubocop:disable AssignmentInCondition
61
+ elsif required = (value == :required) # rubocop:disable Lint/AssignmentInCondition
61
62
  :string
62
63
  end
63
64
  when TrueClass, FalseClass
@@ -68,7 +69,7 @@ class Thor
68
69
  value.class.name.downcase.to_sym
69
70
  end
70
71
 
71
- new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases)
72
+ new(name.to_s, required: required, type: type, default: default, aliases: aliases)
72
73
  end
73
74
 
74
75
  def switch_name
@@ -88,14 +89,27 @@ class Thor
88
89
 
89
90
  sample = "[#{sample}]".dup unless required?
90
91
 
91
- if boolean?
92
- sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-")
92
+ if boolean? && name != "force" && !name.match(/\A(no|skip)[\-_]/)
93
+ sample << ", [#{dasherize('no-' + human_name)}], [#{dasherize('skip-' + human_name)}]"
93
94
  end
94
95
 
96
+ aliases_for_usage.ljust(padding) + sample
97
+ end
98
+
99
+ def aliases_for_usage
95
100
  if aliases.empty?
96
- (" " * padding) << sample
101
+ ""
102
+ else
103
+ "#{aliases.join(', ')}, "
104
+ end
105
+ end
106
+
107
+ def show_default?
108
+ case default
109
+ when TrueClass, FalseClass
110
+ true
97
111
  else
98
- "#{aliases.join(', ')}, #{sample}"
112
+ super
99
113
  end
100
114
  end
101
115
 
@@ -111,7 +125,7 @@ class Thor
111
125
 
112
126
  def validate!
113
127
  raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
114
- validate_default_type! if @check_default_type
128
+ validate_default_type!
115
129
  end
116
130
 
117
131
  def validate_default_type!
@@ -128,7 +142,19 @@ class Thor
128
142
  @default.class.name.downcase.to_sym
129
143
  end
130
144
 
131
- raise ArgumentError, "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type
145
+ expected_type = (@repeatable && @type != :hash) ? :array : @type
146
+
147
+ if default_type != expected_type
148
+ err = "Expected #{expected_type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})"
149
+
150
+ if @check_default_type
151
+ raise ArgumentError, err
152
+ elsif @check_default_type == nil
153
+ Thor.deprecation_warning "#{err}.\n" +
154
+ "This will be rejected in the future unless you explicitly pass the options `check_default_type: false`" +
155
+ " or call `allow_incompatible_default_type!` in your code"
156
+ end
157
+ end
132
158
  end
133
159
 
134
160
  def dasherized?
@@ -142,5 +168,11 @@ class Thor
142
168
  def dasherize(str)
143
169
  (str.length > 1 ? "--" : "-") + str.tr("_", "-")
144
170
  end
171
+
172
+ private
173
+
174
+ def normalize_aliases(aliases)
175
+ Array(aliases).map { |short| short.to_s.sub(/^(?!\-)/, "-") }
176
+ end
145
177
  end
146
178
  end
@@ -1,5 +1,5 @@
1
1
  class Thor
2
- class Options < Arguments #:nodoc: # rubocop:disable ClassLength
2
+ class Options < Arguments #:nodoc:
3
3
  LONG_RE = /^(--\w+(?:-\w+)*)$/
4
4
  SHORT_RE = /^(-[a-z])$/i
5
5
  EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i
@@ -29,8 +29,10 @@ class Thor
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, disable_required_check = false)
32
+ def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false, relations = {})
33
33
  @stop_on_unknown = stop_on_unknown
34
+ @exclusives = (relations[:exclusive_option_names] || []).select{|array| !array.empty?}
35
+ @at_least_ones = (relations[:at_least_one_option_names] || []).select{|array| !array.empty?}
34
36
  @disable_required_check = disable_required_check
35
37
  options = hash_options.values
36
38
  super(options)
@@ -45,12 +47,12 @@ class Thor
45
47
  @switches = {}
46
48
  @extra = []
47
49
  @stopped_parsing_after_extra_index = nil
50
+ @is_treated_as_value = false
48
51
 
49
52
  options.each do |option|
50
53
  @switches[option.switch_name] = option
51
54
 
52
- option.aliases.each do |short|
53
- name = short.to_s.sub(/^(?!\-)/, "-")
55
+ option.aliases.each do |name|
54
56
  @shorts[name] ||= option.switch_name
55
57
  end
56
58
  end
@@ -74,8 +76,19 @@ class Thor
74
76
  end
75
77
  end
76
78
 
77
- def parse(args) # rubocop:disable MethodLength
79
+ def shift
80
+ @is_treated_as_value = false
81
+ super
82
+ end
83
+
84
+ def unshift(arg, is_value: false)
85
+ @is_treated_as_value = is_value
86
+ super(arg)
87
+ end
88
+
89
+ def parse(args) # rubocop:disable Metrics/MethodLength
78
90
  @pile = args.dup
91
+ @is_treated_as_value = false
79
92
  @parsing_options = true
80
93
 
81
94
  while peek
@@ -88,7 +101,10 @@ class Thor
88
101
  when SHORT_SQ_RE
89
102
  unshift($1.split("").map { |f| "-#{f}" })
90
103
  next
91
- when EQ_RE, SHORT_NUM
104
+ when EQ_RE
105
+ unshift($2, is_value: true)
106
+ switch = $1
107
+ when SHORT_NUM
92
108
  unshift($2)
93
109
  switch = $1
94
110
  when LONG_RE, SHORT_RE
@@ -97,7 +113,8 @@ class Thor
97
113
 
98
114
  switch = normalize_switch(switch)
99
115
  option = switch_option(switch)
100
- @assigns[option.human_name] = parse_peek(switch, option)
116
+ result = parse_peek(switch, option)
117
+ assign_result!(option, result)
101
118
  elsif @stop_on_unknown
102
119
  @parsing_options = false
103
120
  @extra << shifted
@@ -116,12 +133,38 @@ class Thor
116
133
  end
117
134
 
118
135
  check_requirement! unless @disable_required_check
136
+ check_exclusive!
137
+ check_at_least_one!
119
138
 
120
139
  assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
121
140
  assigns.freeze
122
141
  assigns
123
142
  end
124
143
 
144
+ def check_exclusive!
145
+ opts = @assigns.keys
146
+ # When option A and B are exclusive, if A and B are given at the same time,
147
+ # the diffrence of argument array size will decrease.
148
+ found = @exclusives.find{ |ex| (ex - opts).size < ex.size - 1 }
149
+ if found
150
+ names = names_to_switch_names(found & opts).map{|n| "'#{n}'"}
151
+ class_name = self.class.name.split("::").last.downcase
152
+ fail ExclusiveArgumentError, "Found exclusive #{class_name} #{names.join(", ")}"
153
+ end
154
+ end
155
+
156
+ def check_at_least_one!
157
+ opts = @assigns.keys
158
+ # When at least one is required of the options A and B,
159
+ # if the both options were not given, none? would be true.
160
+ found = @at_least_ones.find{ |one_reqs| one_reqs.none?{ |o| opts.include? o} }
161
+ if found
162
+ names = names_to_switch_names(found).map{|n| "'#{n}'"}
163
+ class_name = self.class.name.split("::").last.downcase
164
+ fail AtLeastOneRequiredArgumentError, "Not found at least one of required #{class_name} #{names.join(", ")}"
165
+ end
166
+ end
167
+
125
168
  def check_unknown!
126
169
  to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra
127
170
 
@@ -132,11 +175,33 @@ class Thor
132
175
 
133
176
  protected
134
177
 
178
+ # Option names changes to swith name or human name
179
+ def names_to_switch_names(names = [])
180
+ @switches.map do |_, o|
181
+ if names.include? o.name
182
+ o.respond_to?(:switch_name) ? o.switch_name : o.human_name
183
+ else
184
+ nil
185
+ end
186
+ end.compact
187
+ end
188
+
189
+ def assign_result!(option, result)
190
+ if option.repeatable && option.type == :hash
191
+ (@assigns[option.human_name] ||= {}).merge!(result)
192
+ elsif option.repeatable
193
+ (@assigns[option.human_name] ||= []) << result
194
+ else
195
+ @assigns[option.human_name] = result
196
+ end
197
+ end
198
+
135
199
  # Check if the current value in peek is a registered switch.
136
200
  #
137
201
  # Two booleans are returned. The first is true if the current value
138
202
  # starts with a hyphen; the second is true if it is a registered switch.
139
203
  def current_is_switch?
204
+ return [false, false] if @is_treated_as_value
140
205
  case peek
141
206
  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
142
207
  [true, switch?($1)]
@@ -148,6 +213,7 @@ class Thor
148
213
  end
149
214
 
150
215
  def current_is_switch_formatted?
216
+ return false if @is_treated_as_value
151
217
  case peek
152
218
  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
153
219
  true
@@ -157,15 +223,16 @@ class Thor
157
223
  end
158
224
 
159
225
  def current_is_value?
226
+ return true if @is_treated_as_value
160
227
  peek && (!parsing_options? || super)
161
228
  end
162
229
 
163
230
  def switch?(arg)
164
- switch_option(normalize_switch(arg))
231
+ !switch_option(normalize_switch(arg)).nil?
165
232
  end
166
233
 
167
234
  def switch_option(arg)
168
- if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition
235
+ if match = no_or_skip?(arg) # rubocop:disable Lint/AssignmentInCondition
169
236
  @switches[arg] || @switches["--#{match}"]
170
237
  else
171
238
  @switches[arg]
@@ -183,7 +250,8 @@ class Thor
183
250
  @parsing_options
184
251
  end
185
252
 
186
- # Parse boolean values which can be given as --foo=true, --foo or --no-foo.
253
+ # Parse boolean values which can be given as --foo=true or --foo for true values, or
254
+ # --foo=false, --no-foo or --skip-foo for false values.
187
255
  #
188
256
  def parse_boolean(switch)
189
257
  if current_is_value?
@@ -194,7 +262,7 @@ class Thor
194
262
  shift
195
263
  false
196
264
  else
197
- !no_or_skip?(switch)
265
+ @switches.key?(switch) || !no_or_skip?(switch)
198
266
  end
199
267
  else
200
268
  @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)
@@ -40,7 +41,7 @@ instance_eval do
40
41
  def task(*)
41
42
  task = super
42
43
 
43
- if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
44
+ if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable Lint/AssignmentInCondition
44
45
  non_namespaced_name = task.name.split(":").last
45
46
 
46
47
  description = non_namespaced_name
@@ -58,7 +59,7 @@ instance_eval do
58
59
  end
59
60
 
60
61
  def namespace(name)
61
- if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
62
+ if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable Lint/AssignmentInCondition
62
63
  const_name = Thor::Util.camel_case(name.to_s).to_sym
63
64
  klass.const_set(const_name, Class.new(Thor))
64
65
  new_klass = klass.const_get(const_name)
data/lib/thor/runner.rb CHANGED
@@ -1,12 +1,11 @@
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
4
  require "yaml"
6
- require "digest/md5"
5
+ require "digest/sha2"
7
6
  require "pathname"
8
7
 
9
- class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
8
+ class Thor::Runner < Thor #:nodoc:
10
9
  map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
11
10
 
12
11
  def self.banner(command, all = false, subcommand = false)
@@ -24,7 +23,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
24
23
  initialize_thorfiles(meth)
25
24
  klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
26
25
  self.class.handle_no_command_error(command, false) if klass.nil?
27
- klass.start(["-h", command].compact, :shell => shell)
26
+ klass.start(["-h", command].compact, shell: shell)
28
27
  else
29
28
  super
30
29
  end
@@ -39,30 +38,42 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
39
38
  klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
40
39
  self.class.handle_no_command_error(command, false) if klass.nil?
41
40
  args.unshift(command) if command
42
- klass.start(args, :shell => shell)
41
+ klass.start(args, shell: shell)
43
42
  end
44
43
 
45
44
  desc "install NAME", "Install an optionally named Thor file into your system commands"
46
- method_options :as => :string, :relative => :boolean, :force => :boolean
47
- def install(name) # rubocop:disable MethodLength
45
+ method_options as: :string, relative: :boolean, force: :boolean
46
+ def install(name) # rubocop:disable Metrics/MethodLength
48
47
  initialize_thorfiles
49
48
 
50
- # If a directory name is provided as the argument, look for a 'main.thor'
51
- # command in said directory.
52
- begin
53
- if File.directory?(File.expand_path(name))
54
- base = File.join(name, "main.thor")
55
- package = :directory
56
- contents = open(base, &:read)
57
- else
58
- base = name
59
- package = :file
60
- contents = open(name, &:read)
49
+ is_uri = name =~ %r{^https?\://}
50
+
51
+ if is_uri
52
+ base = name
53
+ package = :file
54
+ require "open-uri"
55
+ begin
56
+ contents = URI.open(name, &:read)
57
+ rescue OpenURI::HTTPError
58
+ raise Error, "Error opening URI '#{name}'"
59
+ end
60
+ else
61
+ # If a directory name is provided as the argument, look for a 'main.thor'
62
+ # command in said directory.
63
+ begin
64
+ if File.directory?(File.expand_path(name))
65
+ base = File.join(name, "main.thor")
66
+ package = :directory
67
+ contents = File.open(base, &:read)
68
+ else
69
+ base = name
70
+ package = :file
71
+ require "open-uri"
72
+ contents = URI.open(name, &:read)
73
+ end
74
+ rescue Errno::ENOENT
75
+ raise Error, "Error opening file '#{name}'"
61
76
  end
62
- rescue OpenURI::HTTPError
63
- raise Error, "Error opening URI '#{name}'"
64
- rescue Errno::ENOENT
65
- raise Error, "Error opening file '#{name}'"
66
77
  end
67
78
 
68
79
  say "Your Thorfile contains:"
@@ -83,16 +94,16 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
83
94
  as = basename if as.empty?
84
95
  end
85
96
 
86
- location = if options[:relative] || name =~ %r{^https?://}
97
+ location = if options[:relative] || is_uri
87
98
  name
88
99
  else
89
100
  File.expand_path(name)
90
101
  end
91
102
 
92
103
  thor_yaml[as] = {
93
- :filename => Digest::MD5.hexdigest(name + as),
94
- :location => location,
95
- :namespaces => Thor::Util.namespaces_in_content(contents, base)
104
+ filename: Digest::SHA256.hexdigest(name + as),
105
+ location: location,
106
+ namespaces: Thor::Util.namespaces_in_content(contents, base)
96
107
  }
97
108
 
98
109
  save_yaml(thor_yaml)
@@ -111,7 +122,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
111
122
 
112
123
  desc "version", "Show Thor version"
113
124
  def version
114
- require "thor/version"
125
+ require_relative "version"
115
126
  say "Thor #{Thor::VERSION}"
116
127
  end
117
128
 
@@ -153,14 +164,14 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
153
164
  end
154
165
 
155
166
  desc "installed", "List the installed Thor modules and commands"
156
- method_options :internal => :boolean
167
+ method_options internal: :boolean
157
168
  def installed
158
169
  initialize_thorfiles(nil, true)
159
170
  display_klasses(true, options["internal"])
160
171
  end
161
172
 
162
173
  desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)"
163
- method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean
174
+ method_options substring: :boolean, group: :string, all: :boolean, debug: :boolean
164
175
  def list(search = "")
165
176
  initialize_thorfiles
166
177
 
@@ -302,7 +313,7 @@ private
302
313
  say shell.set_color(namespace, :blue, true)
303
314
  say "-" * namespace.size
304
315
 
305
- print_table(list, :truncate => true)
316
+ print_table(list, truncate: true)
306
317
  say
307
318
  end
308
319
  alias_method :display_tasks, :display_commands