thor 0.16.0 → 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.
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
@@ -1,17 +1,18 @@
1
1
  class Thor
2
2
  module Invocation
3
3
  def self.included(base) #:nodoc:
4
+ super(base)
4
5
  base.extend ClassMethods
5
6
  end
6
7
 
7
8
  module ClassMethods
8
9
  # This method is responsible for receiving a name and find the proper
9
- # class and task for it. The key is an optional parameter which is
10
+ # class and command for it. The key is an optional parameter which is
10
11
  # available only in class methods invocations (i.e. in Thor::Group).
11
12
  def prepare_for_invocation(key, name) #:nodoc:
12
13
  case name
13
14
  when Symbol, String
14
- Thor::Util.find_class_and_task_by_namespace(name.to_s, !key)
15
+ Thor::Util.find_class_and_command_by_namespace(name.to_s, !key)
15
16
  else
16
17
  name
17
18
  end
@@ -19,32 +20,37 @@ class Thor
19
20
  end
20
21
 
21
22
  # Make initializer aware of invocations and the initialization args.
22
- def initialize(args=[], options={}, config={}, &block) #:nodoc:
23
- @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
24
- @_initializer = [ args, options, config ]
23
+ def initialize(args = [], options = {}, config = {}, &block) #:nodoc:
24
+ @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] }
25
+ @_initializer = [args, options, config]
25
26
  super
26
27
  end
27
28
 
28
- # Receives a name and invokes it. The name can be a string (either "task" or
29
- # "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task
30
- # cannot be guessed by name, it can also be supplied as second argument.
29
+ # Make the current command chain accessible with in a Thor-(sub)command
30
+ def current_command_chain
31
+ @_invocations.values.flatten.map(&:to_sym)
32
+ end
33
+
34
+ # Receives a name and invokes it. The name can be a string (either "command" or
35
+ # "namespace:command"), a Thor::Command, a Class or a Thor instance. If the
36
+ # command cannot be guessed by name, it can also be supplied as second argument.
31
37
  #
32
38
  # You can also supply the arguments, options and configuration values for
33
- # the task to be invoked, if none is given, the same values used to
39
+ # the command to be invoked, if none is given, the same values used to
34
40
  # initialize the invoker are used to initialize the invoked.
35
41
  #
36
- # When no name is given, it will invoke the default task of the current class.
42
+ # When no name is given, it will invoke the default command of the current class.
37
43
  #
38
44
  # ==== Examples
39
45
  #
40
46
  # class A < Thor
41
47
  # def foo
42
48
  # invoke :bar
43
- # invoke "b:hello", ["José"]
49
+ # invoke "b:hello", ["Erik"]
44
50
  # end
45
51
  #
46
52
  # def bar
47
- # invoke "b:hello", ["José"]
53
+ # invoke "b:hello", ["Erik"]
48
54
  # end
49
55
  # end
50
56
  #
@@ -54,16 +60,16 @@ class Thor
54
60
  # end
55
61
  # end
56
62
  #
57
- # You can notice that the method "foo" above invokes two tasks: "bar",
63
+ # You can notice that the method "foo" above invokes two commands: "bar",
58
64
  # which belongs to the same class and "hello" which belongs to the class B.
59
65
  #
60
- # By using an invocation system you ensure that a task is invoked only once.
66
+ # By using an invocation system you ensure that a command is invoked only once.
61
67
  # In the example above, invoking "foo" will invoke "b:hello" just once, even
62
68
  # if it's invoked later by "bar" method.
63
69
  #
64
70
  # When class A invokes class B, all arguments used on A initialization are
65
71
  # supplied to B. This allows lazy parse of options. Let's suppose you have
66
- # some rspec tasks:
72
+ # some rspec commands:
67
73
  #
68
74
  # class Rspec < Thor::Group
69
75
  # class_option :mock_framework, :type => :string, :default => :rr
@@ -93,37 +99,39 @@ class Thor
93
99
  #
94
100
  # invoke Rspec::RR, [], :style => :foo
95
101
  #
96
- def invoke(name=nil, *args)
102
+ def invoke(name = nil, *args)
97
103
  if name.nil?
98
104
  warn "[Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}"
99
105
  return invoke_all
100
106
  end
101
107
 
102
- args.unshift(nil) if Array === args.first || NilClass === args.first
103
- task, args, opts, config = args
108
+ args.unshift(nil) if args.first.is_a?(Array) || args.first.nil?
109
+ command, args, opts, config = args
104
110
 
105
- klass, task = _retrieve_class_and_task(name, task)
111
+ klass, command = _retrieve_class_and_command(name, command)
112
+ raise "Missing Thor class for invoke #{name}" unless klass
106
113
  raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
107
114
 
108
115
  args, opts, config = _parse_initialization_options(args, opts, config)
109
- klass.send(:dispatch, task, args, opts, config) do |instance|
116
+ klass.send(:dispatch, command, args, opts, config) do |instance|
110
117
  instance.parent_options = options
111
118
  end
112
119
  end
113
120
 
114
- # Invoke the given task if the given args.
115
- def invoke_task(task, *args) #:nodoc:
121
+ # Invoke the given command if the given args.
122
+ def invoke_command(command, *args) #:nodoc:
116
123
  current = @_invocations[self.class]
117
124
 
118
- unless current.include?(task.name)
119
- current << task.name
120
- task.run(self, *args)
125
+ unless current.include?(command.name)
126
+ current << command.name
127
+ command.run(self, *args)
121
128
  end
122
129
  end
130
+ alias_method :invoke_task, :invoke_command
123
131
 
124
- # Invoke all tasks for the current instance.
132
+ # Invoke all commands for the current instance.
125
133
  def invoke_all #:nodoc:
126
- self.class.all_tasks.map { |_, task| invoke_task(task) }
134
+ self.class.all_commands.map { |_, command| invoke_command(command) }
127
135
  end
128
136
 
129
137
  # Invokes using shell padding.
@@ -131,40 +139,40 @@ class Thor
131
139
  with_padding { invoke(*args) }
132
140
  end
133
141
 
134
- protected
142
+ protected
135
143
 
136
- # Configuration values that are shared between invocations.
137
- def _shared_configuration #:nodoc:
138
- { :invocations => @_invocations }
139
- end
144
+ # Configuration values that are shared between invocations.
145
+ def _shared_configuration #:nodoc:
146
+ {:invocations => @_invocations}
147
+ end
140
148
 
141
- # This method simply retrieves the class and task to be invoked.
142
- # If the name is nil or the given name is a task in the current class,
143
- # use the given name and return self as class. Otherwise, call
144
- # prepare_for_invocation in the current class.
145
- def _retrieve_class_and_task(name, sent_task=nil) #:nodoc:
146
- case
147
- when name.nil?
148
- [self.class, nil]
149
- when self.class.all_tasks[name.to_s]
150
- [self.class, name.to_s]
151
- else
152
- klass, task = self.class.prepare_for_invocation(nil, name)
153
- [klass, task || sent_task]
154
- end
149
+ # This method simply retrieves the class and command to be invoked.
150
+ # If the name is nil or the given name is a command in the current class,
151
+ # use the given name and return self as class. Otherwise, call
152
+ # prepare_for_invocation in the current class.
153
+ def _retrieve_class_and_command(name, sent_command = nil) #:nodoc:
154
+ if name.nil?
155
+ [self.class, nil]
156
+ elsif self.class.all_commands[name.to_s]
157
+ [self.class, name.to_s]
158
+ else
159
+ klass, command = self.class.prepare_for_invocation(nil, name)
160
+ [klass, command || sent_command]
155
161
  end
162
+ end
163
+ alias_method :_retrieve_class_and_task, :_retrieve_class_and_command
156
164
 
157
- # Initialize klass using values stored in the @_initializer.
158
- def _parse_initialization_options(args, opts, config) #:nodoc:
159
- stored_args, stored_opts, stored_config = @_initializer
165
+ # Initialize klass using values stored in the @_initializer.
166
+ def _parse_initialization_options(args, opts, config) #:nodoc:
167
+ stored_args, stored_opts, stored_config = @_initializer
160
168
 
161
- args ||= stored_args.dup
162
- opts ||= stored_opts.dup
169
+ args ||= stored_args.dup
170
+ opts ||= stored_opts.dup
163
171
 
164
- config ||= {}
165
- config = stored_config.merge(_shared_configuration).merge!(config)
172
+ config ||= {}
173
+ config = stored_config.merge(_shared_configuration).merge!(config)
166
174
 
167
- [ args, opts, config ]
168
- end
175
+ [args, opts, config]
176
+ end
169
177
  end
170
178
  end
@@ -0,0 +1,37 @@
1
+ class Thor
2
+ module LineEditor
3
+ class Basic
4
+ attr_reader :prompt, :options
5
+
6
+ def self.available?
7
+ true
8
+ end
9
+
10
+ def initialize(prompt, options)
11
+ @prompt = prompt
12
+ @options = options
13
+ end
14
+
15
+ def readline
16
+ $stdout.print(prompt)
17
+ get_input
18
+ end
19
+
20
+ private
21
+
22
+ def get_input
23
+ if echo?
24
+ $stdin.gets
25
+ else
26
+ # Lazy-load io/console since it is gem-ified as of 2.3
27
+ require "io/console"
28
+ $stdin.noecho(&:gets)
29
+ end
30
+ end
31
+
32
+ def echo?
33
+ options.fetch(:echo, true)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,88 @@
1
+ class Thor
2
+ module LineEditor
3
+ class Readline < Basic
4
+ def self.available?
5
+ begin
6
+ require "readline"
7
+ rescue LoadError
8
+ end
9
+
10
+ Object.const_defined?(:Readline)
11
+ end
12
+
13
+ def readline
14
+ if echo?
15
+ ::Readline.completion_append_character = nil
16
+ # rb-readline does not allow Readline.completion_proc= to receive nil.
17
+ if complete = completion_proc
18
+ ::Readline.completion_proc = complete
19
+ end
20
+ ::Readline.readline(prompt, add_to_history?)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def add_to_history?
29
+ options.fetch(:add_to_history, true)
30
+ end
31
+
32
+ def completion_proc
33
+ if use_path_completion?
34
+ proc { |text| PathCompletion.new(text).matches }
35
+ elsif completion_options.any?
36
+ proc do |text|
37
+ completion_options.select { |option| option.start_with?(text) }
38
+ end
39
+ end
40
+ end
41
+
42
+ def completion_options
43
+ options.fetch(:limited_to, [])
44
+ end
45
+
46
+ def use_path_completion?
47
+ options.fetch(:path, false)
48
+ end
49
+
50
+ class PathCompletion
51
+ attr_reader :text
52
+ private :text
53
+
54
+ def initialize(text)
55
+ @text = text
56
+ end
57
+
58
+ def matches
59
+ relative_matches
60
+ end
61
+
62
+ private
63
+
64
+ def relative_matches
65
+ absolute_matches.map { |path| path.sub(base_path, "") }
66
+ end
67
+
68
+ def absolute_matches
69
+ Dir[glob_pattern].map do |path|
70
+ if File.directory?(path)
71
+ "#{path}/"
72
+ else
73
+ path
74
+ end
75
+ end
76
+ end
77
+
78
+ def glob_pattern
79
+ "#{base_path}#{text}*"
80
+ end
81
+
82
+ def base_path
83
+ "#{Dir.pwd}/"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "line_editor/basic"
2
+ require_relative "line_editor/readline"
3
+
4
+ class Thor
5
+ module LineEditor
6
+ def self.readline(prompt, options = {})
7
+ best_available.new(prompt, options).readline
8
+ end
9
+
10
+ def self.best_available
11
+ [
12
+ Thor::LineEditor::Readline,
13
+ Thor::LineEditor::Basic
14
+ ].detect(&:available?)
15
+ end
16
+ end
17
+ end
@@ -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 > 0
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
@@ -1,11 +1,11 @@
1
1
  class Thor
2
2
  class Argument #:nodoc:
3
- VALID_TYPES = [ :numeric, :hash, :array, :string ]
3
+ VALID_TYPES = [:numeric, :hash, :array, :string]
4
4
 
5
5
  attr_reader :name, :description, :enum, :required, :type, :default, :banner
6
- alias :human_name :name
6
+ alias_method :human_name, :name
7
7
 
8
- def initialize(name, options={})
8
+ def initialize(name, options = {})
9
9
  class_name = self.class.name.split("::").last
10
10
 
11
11
  type = options[:type]
@@ -41,34 +41,30 @@ class Thor
41
41
  end
42
42
  end
43
43
 
44
- protected
44
+ protected
45
45
 
46
- def validate!
47
- if required? && !default.nil?
48
- raise ArgumentError, "An argument cannot be required and have default value."
49
- elsif @enum && !@enum.is_a?(Array)
50
- raise ArgumentError, "An argument cannot have an enum other than an array."
51
- end
52
- end
46
+ def validate!
47
+ 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)
49
+ end
53
50
 
54
- def valid_type?(type)
55
- self.class::VALID_TYPES.include?(type.to_sym)
56
- end
51
+ def valid_type?(type)
52
+ self.class::VALID_TYPES.include?(type.to_sym)
53
+ end
57
54
 
58
- def default_banner
59
- case type
60
- when :boolean
61
- nil
62
- when :string, :default
63
- human_name.upcase
64
- when :numeric
65
- "N"
66
- when :hash
67
- "key:value"
68
- when :array
69
- "one two three"
70
- end
55
+ def default_banner
56
+ case type
57
+ when :boolean
58
+ nil
59
+ when :string, :default
60
+ human_name.upcase
61
+ when :numeric
62
+ "N"
63
+ when :hash
64
+ "key:value"
65
+ when :array
66
+ "one two three"
71
67
  end
72
-
68
+ end
73
69
  end
74
70
  end