thor 0.13.7 → 0.13.8

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,5 +1,6 @@
1
1
  == 0.13, released 2010-02-03
2
2
 
3
+ * Added :lazy_default which is only triggered if a switch is given
3
4
  * Added Thor::Shell::HTML
4
5
  * Decoupled Thor::Group and Thor, so it's easier to vendor
5
6
  * Added check_unknown_options! in case you want error messages to be raised in valid switches
@@ -23,6 +23,7 @@ class Thor
23
23
  # ==== Parameters
24
24
  # usage<String>
25
25
  # description<String>
26
+ # options<String>
26
27
  #
27
28
  def desc(usage, description, options={})
28
29
  if options[:for]
@@ -30,7 +31,7 @@ class Thor
30
31
  task.usage = usage if usage
31
32
  task.description = description if description
32
33
  else
33
- @usage, @desc = usage, description
34
+ @usage, @desc, @hide = usage, description, options[:hide] || false
34
35
  end
35
36
  end
36
37
 
@@ -135,6 +136,7 @@ class Thor
135
136
  #
136
137
  def start(original_args=ARGV, config={})
137
138
  @@original_args = original_args
139
+
138
140
  super do |given_args|
139
141
  meth = given_args.first.to_s
140
142
 
@@ -154,7 +156,7 @@ class Thor
154
156
  args, opts = given_args, {}
155
157
  end
156
158
 
157
- task ||= Thor::Task::Dynamic.new(meth)
159
+ task ||= Thor::DynamicTask.new(meth)
158
160
  trailing = args[Range.new(arguments.size, -1)]
159
161
  new(args, opts, config).invoke(task, trailing || [])
160
162
  end
@@ -204,11 +206,12 @@ class Thor
204
206
  # Returns tasks ready to be printed.
205
207
  def printable_tasks(all = true, subcommand = false)
206
208
  (all ? all_tasks : tasks).map do |_, task|
209
+ next if task.hidden?
207
210
  item = []
208
211
  item << banner(task, false, subcommand)
209
212
  item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
210
213
  item
211
- end
214
+ end.compact
212
215
  end
213
216
 
214
217
  def subcommands
@@ -239,8 +242,9 @@ class Thor
239
242
 
240
243
  def create_task(meth) #:nodoc:
241
244
  if @usage && @desc
242
- tasks[meth.to_s] = Thor::Task.new(meth, @desc, @long_desc, @usage, method_options)
243
- @usage, @desc, @long_desc, @method_options = nil
245
+ base_class = @hide ? Thor::HiddenTask : Thor::Task
246
+ tasks[meth.to_s] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
247
+ @usage, @desc, @long_desc, @method_options, @hide = nil
244
248
  true
245
249
  elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing
246
250
  true
@@ -336,6 +336,7 @@ class Thor
336
336
  def no_tasks
337
337
  @no_tasks = true
338
338
  yield
339
+ ensure
339
340
  @no_tasks = false
340
341
  end
341
342
 
@@ -449,7 +450,7 @@ class Thor
449
450
  def build_option(name, options, scope) #:nodoc:
450
451
  scope[name] = Thor::Option.new(name, options[:desc], options[:required],
451
452
  options[:type], options[:default], options[:banner],
452
- options[:group], options[:aliases])
453
+ options[:lazy_default], options[:group], options[:aliases])
453
454
  end
454
455
 
455
456
  # Receives a hash of options, parse them and add to the scope. This is a
@@ -233,7 +233,7 @@ class Thor::Group
233
233
 
234
234
  # Represents the whole class as a task.
235
235
  def self_task #:nodoc:
236
- Thor::Task::Dynamic.new(self.namespace, class_options)
236
+ Thor::DynamicTask.new(self.namespace, class_options)
237
237
  end
238
238
 
239
239
  def baseclass #:nodoc:
@@ -156,7 +156,7 @@ class Thor
156
156
  raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
157
157
 
158
158
  task ||= klass.default_task if klass.respond_to?(:default_task)
159
- task = klass.all_tasks[task.to_s] || Thor::Task::Dynamic.new(task) if task && !task.is_a?(Thor::Task)
159
+ task = klass.all_tasks[task.to_s] || Thor::DynamicTask.new(task) if task && !task.is_a?(Thor::Task)
160
160
  task
161
161
  end
162
162
 
@@ -31,10 +31,10 @@ class Thor
31
31
 
32
32
  def show_default?
33
33
  case default
34
- when Array, String, Hash
35
- !default.empty?
36
- else
37
- default
34
+ when Array, String, Hash
35
+ !default.empty?
36
+ else
37
+ default
38
38
  end
39
39
  end
40
40
 
@@ -45,21 +45,21 @@ class Thor
45
45
  end
46
46
 
47
47
  def valid_type?(type)
48
- VALID_TYPES.include?(type.to_sym)
48
+ self.class::VALID_TYPES.include?(type.to_sym)
49
49
  end
50
50
 
51
51
  def default_banner
52
52
  case type
53
- when :boolean
54
- nil
55
- when :string, :default
56
- human_name.upcase
57
- when :numeric
58
- "N"
59
- when :hash
60
- "key:value"
61
- when :array
62
- "one two three"
53
+ when :boolean
54
+ nil
55
+ when :string, :default
56
+ human_name.upcase
57
+ when :numeric
58
+ "N"
59
+ when :hash
60
+ "key:value"
61
+ when :array
62
+ "one two three"
63
63
  end
64
64
  end
65
65
 
@@ -1,13 +1,14 @@
1
1
  class Thor
2
2
  class Option < Argument #:nodoc:
3
- attr_reader :aliases, :group
3
+ attr_reader :aliases, :group, :lazy_default
4
4
 
5
5
  VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
6
6
 
7
- def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil)
7
+ def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, lazy_default=nil, group=nil, aliases=nil)
8
8
  super(name, description, required, type, default, banner)
9
- @aliases = [*aliases].compact
10
- @group = group.to_s.capitalize if group
9
+ @lazy_default = lazy_default
10
+ @group = group.to_s.capitalize if group
11
+ @aliases = [*aliases].compact
11
12
  end
12
13
 
13
14
  # This parse quick options given as method_options. It makes several
@@ -48,23 +49,22 @@ class Thor
48
49
  default = value
49
50
 
50
51
  type = case value
51
- when Symbol
52
- default = nil
53
-
54
- if VALID_TYPES.include?(value)
55
- value
56
- elsif required = (value == :required)
57
- :string
58
- end
59
- when TrueClass, FalseClass
60
- :boolean
61
- when Numeric
62
- :numeric
63
- when Hash, Array, String
64
- value.class.name.downcase.to_sym
52
+ when Symbol
53
+ default = nil
54
+ if VALID_TYPES.include?(value)
55
+ value
56
+ elsif required = (value == :required)
57
+ :string
58
+ end
59
+ when TrueClass, FalseClass
60
+ :boolean
61
+ when Numeric
62
+ :numeric
63
+ when Hash, Array, String
64
+ value.class.name.downcase.to_sym
65
65
  end
66
66
 
67
- self.new(name.to_s, nil, required, type, default, nil, nil, aliases)
67
+ self.new(name.to_s, nil, required, type, default, nil, nil, nil, aliases)
68
68
  end
69
69
 
70
70
  def switch_name
@@ -91,38 +91,30 @@ class Thor
91
91
  end
92
92
  end
93
93
 
94
- # Allow some type predicates as: boolean?, string? and etc.
95
- #
96
- def method_missing(method, *args, &block)
97
- given = method.to_s.sub(/\?$/, '').to_sym
98
- if valid_type?(given)
99
- self.type == given
100
- else
101
- super
102
- end
94
+ VALID_TYPES.each do |type|
95
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
96
+ def #{type}?
97
+ self.type == #{type.inspect}
98
+ end
99
+ RUBY
103
100
  end
104
101
 
105
- protected
106
-
107
- def validate!
108
- raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
109
- end
110
-
111
- def valid_type?(type)
112
- VALID_TYPES.include?(type.to_sym)
113
- end
102
+ protected
114
103
 
115
- def dasherized?
116
- name.index('-') == 0
117
- end
104
+ def validate!
105
+ raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
106
+ end
118
107
 
119
- def undasherize(str)
120
- str.sub(/^-{1,2}/, '')
121
- end
108
+ def dasherized?
109
+ name.index('-') == 0
110
+ end
122
111
 
123
- def dasherize(str)
124
- (str.length > 1 ? "--" : "-") + str.gsub('_', '-')
125
- end
112
+ def undasherize(str)
113
+ str.sub(/^-{1,2}/, '')
114
+ end
126
115
 
116
+ def dasherize(str)
117
+ (str.length > 1 ? "--" : "-") + str.gsub('_', '-')
118
+ end
127
119
  end
128
120
  end
@@ -150,7 +150,7 @@ class Thor
150
150
  return nil # User set value to nil
151
151
  elsif option.string? && !option.required?
152
152
  # Return the default if there is one, else the human name
153
- return option.default || option.human_name
153
+ return option.lazy_default || option.default || option.human_name
154
154
  else
155
155
  raise MalformattedArgumentError, "No value provided for option '#{switch}'"
156
156
  end
@@ -2,21 +2,6 @@ class Thor
2
2
  class Task < Struct.new(:name, :description, :long_description, :usage, :options)
3
3
  FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
4
4
 
5
- # A dynamic task that handles method missing scenarios.
6
- class Dynamic < Task
7
- def initialize(name, options=nil)
8
- super(name.to_s, "A dynamically-generated task", name.to_s, name.to_s, options)
9
- end
10
-
11
- def run(instance, args=[])
12
- if (instance.methods & [name.to_s, name.to_sym]).empty?
13
- super
14
- else
15
- instance.class.handle_no_task_error(name)
16
- end
17
- end
18
- end
19
-
20
5
  def initialize(name, description, long_description, usage, options=nil)
21
6
  super(name.to_s, description, long_description, usage, options || {})
22
7
  end
@@ -26,6 +11,10 @@ class Thor
26
11
  self.options = other.options.dup if other.options
27
12
  end
28
13
 
14
+ def hidden?
15
+ false
16
+ end
17
+
29
18
  # By default, a task invokes a method in the thor class. You can change this
30
19
  # implementation to create custom tasks.
31
20
  def run(instance, args=[])
@@ -42,9 +31,8 @@ class Thor
42
31
  # Returns the formatted usage by injecting given required arguments
43
32
  # and required options into the given usage.
44
33
  def formatted_usage(klass, namespace = true, subcommand = false)
45
- namespace = klass.namespace unless namespace == false
46
-
47
34
  if namespace
35
+ namespace = klass.namespace
48
36
  formatted = "#{namespace.gsub(/^(default)/,'')}:"
49
37
  formatted.sub!(/.$/, ' ') if subcommand
50
38
  end
@@ -67,36 +55,60 @@ class Thor
67
55
  formatted.strip
68
56
  end
69
57
 
70
- protected
58
+ protected
71
59
 
72
- def not_debugging?(instance)
73
- !(instance.class.respond_to?(:debugging) && instance.class.debugging)
74
- end
60
+ def not_debugging?(instance)
61
+ !(instance.class.respond_to?(:debugging) && instance.class.debugging)
62
+ end
75
63
 
76
- def required_options
77
- @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
78
- end
64
+ def required_options
65
+ @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
66
+ end
79
67
 
80
- # Given a target, checks if this class name is not a private/protected method.
81
- def public_method?(instance) #:nodoc:
82
- collection = instance.private_methods + instance.protected_methods
83
- (collection & [name.to_s, name.to_sym]).empty?
84
- end
68
+ # Given a target, checks if this class name is not a private/protected method.
69
+ def public_method?(instance) #:nodoc:
70
+ collection = instance.private_methods + instance.protected_methods
71
+ (collection & [name.to_s, name.to_sym]).empty?
72
+ end
85
73
 
86
- def sans_backtrace(backtrace, caller) #:nodoc:
87
- saned = backtrace.reject { |frame| frame =~ FILE_REGEXP }
88
- saned -= caller
89
- end
74
+ def sans_backtrace(backtrace, caller) #:nodoc:
75
+ saned = backtrace.reject { |frame| frame =~ FILE_REGEXP }
76
+ saned -= caller
77
+ end
90
78
 
91
- def handle_argument_error?(instance, error, caller)
92
- not_debugging?(instance) && error.message =~ /wrong number of arguments/ &&
93
- sans_backtrace(error.backtrace, caller).empty?
79
+ def handle_argument_error?(instance, error, caller)
80
+ not_debugging?(instance) && error.message =~ /wrong number of arguments/ && begin
81
+ saned = sans_backtrace(error.backtrace, caller)
82
+ # Ruby 1.9 always include the called method in the backtrace
83
+ saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
94
84
  end
85
+ end
95
86
 
96
- def handle_no_method_error?(instance, error, caller)
97
- not_debugging?(instance) &&
98
- error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
99
- end
87
+ def handle_no_method_error?(instance, error, caller)
88
+ not_debugging?(instance) &&
89
+ error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
90
+ end
91
+ end
100
92
 
93
+ # A task that is hidden in help messages but still invocable.
94
+ class HiddenTask < Task
95
+ def hidden?
96
+ true
97
+ end
98
+ end
99
+
100
+ # A dynamic task that handles method missing scenarios.
101
+ class DynamicTask < Task
102
+ def initialize(name, options=nil)
103
+ super(name.to_s, "A dynamically-generated task", name.to_s, name.to_s, options)
104
+ end
105
+
106
+ def run(instance, args=[])
107
+ if (instance.methods & [name.to_s, name.to_sym]).empty?
108
+ super
109
+ else
110
+ instance.class.handle_no_task_error(name)
111
+ end
112
+ end
101
113
  end
102
114
  end
@@ -1,3 +1,3 @@
1
1
  class Thor
2
- VERSION = "0.13.7".freeze
2
+ VERSION = "0.13.8".freeze
3
3
  end
@@ -26,6 +26,11 @@ class MyScript < Thor
26
26
  [type]
27
27
  end
28
28
 
29
+ desc "hidden TYPE", "this is hidden", :hide => true
30
+ def hidden(type)
31
+ [type]
32
+ end
33
+
29
34
  desc "foo BAR", <<END
30
35
  do some fooing
31
36
  This is more info!
@@ -67,6 +72,7 @@ END
67
72
  end
68
73
 
69
74
  method_options :all => :boolean
75
+ method_option :lazy, :lazy_default => "yes"
70
76
  desc "with_optional NAME", "invoke with optional name"
71
77
  def with_optional(name=nil)
72
78
  [ name, options ]
@@ -6,8 +6,8 @@ describe Thor::Option do
6
6
  Thor::Option.parse(key, value)
7
7
  end
8
8
 
9
- def option(name, description=nil, required=false, type=nil, default=nil, banner=nil, group=nil, aliases=[])
10
- @option ||= Thor::Option.new(name, description, required, type, default, banner, group, aliases)
9
+ def option(name, *args)
10
+ @option ||= Thor::Option.new(name, *args)
11
11
  end
12
12
 
13
13
  describe "#parse" do
@@ -10,7 +10,10 @@ require 'rdoc'
10
10
  require 'diff/lcs' # You need diff/lcs installed to run specs (but not to run Thor).
11
11
  require 'fakeweb' # You need fakeweb installed to run specs (but not to run Thor).
12
12
 
13
+ # Set shell to basic
14
+ $0 = "thor"
13
15
  $thor_runner = true
16
+ Thor::Base.shell = Thor::Shell::Basic
14
17
 
15
18
  # Load fixtures
16
19
  load File.join(File.dirname(__FILE__), "fixtures", "task.thor")
@@ -18,10 +21,6 @@ load File.join(File.dirname(__FILE__), "fixtures", "group.thor")
18
21
  load File.join(File.dirname(__FILE__), "fixtures", "script.thor")
19
22
  load File.join(File.dirname(__FILE__), "fixtures", "invoke.thor")
20
23
 
21
- # Set shell to basic
22
- $0 = "thor"
23
- Thor::Base.shell = Thor::Shell::Basic
24
-
25
24
  Kernel.module_eval do
26
25
  alias_method :must, :should
27
26
  alias_method :must_not, :should_not
@@ -31,16 +31,16 @@ describe Thor::Task do
31
31
 
32
32
  describe "#dynamic" do
33
33
  it "creates a dynamic task with the given name" do
34
- Thor::Task::Dynamic.new('task').name.must == 'task'
35
- Thor::Task::Dynamic.new('task').description.must == 'A dynamically-generated task'
36
- Thor::Task::Dynamic.new('task').usage.must == 'task'
37
- Thor::Task::Dynamic.new('task').options.must == {}
34
+ Thor::DynamicTask.new('task').name.must == 'task'
35
+ Thor::DynamicTask.new('task').description.must == 'A dynamically-generated task'
36
+ Thor::DynamicTask.new('task').usage.must == 'task'
37
+ Thor::DynamicTask.new('task').options.must == {}
38
38
  end
39
39
 
40
40
  it "does not invoke an existing method" do
41
41
  mock = mock()
42
42
  mock.class.should_receive(:handle_no_task_error).with("to_s")
43
- Thor::Task::Dynamic.new('to_s').run(mock)
43
+ Thor::DynamicTask.new('to_s').run(mock)
44
44
  end
45
45
  end
46
46
 
@@ -8,6 +8,17 @@ describe Thor do
8
8
  options.must == { "force" => true }
9
9
  end
10
10
 
11
+ it "sets method_option with given parameters" do
12
+ arg, options = MyScript.start(["with_optional"])
13
+ options.must == {}
14
+
15
+ arg, options = MyScript.start(["with_optional", "--lazy"])
16
+ options.must == { "lazy" => "yes" }
17
+
18
+ arg, options = MyScript.start(["with_optional", "--lazy", "yesyes!"])
19
+ options.must == { "lazy" => "yesyes!" }
20
+ end
21
+
11
22
  describe "when :for is supplied" do
12
23
  it "updates an already defined task" do
13
24
  args, options = MyChildScript.start(["animal", "horse", "--other=fish"])
@@ -66,12 +77,19 @@ describe Thor do
66
77
  end
67
78
 
68
79
  describe "#desc" do
69
- before(:all) do
70
- @content = capture(:stdout) { MyScript.start(["help"]) }
80
+ it "provides description for a task" do
81
+ content = capture(:stdout) { MyScript.start(["help"]) }
82
+ content.must =~ /thor my_script:zoo\s+# zoo around/m
71
83
  end
72
84
 
73
- it "provides description for a task" do
74
- @content.must =~ /zoo\s+# zoo around/m
85
+ it "provides no namespace if $thor_runner is false" do
86
+ begin
87
+ $thor_runner = false
88
+ content = capture(:stdout) { MyScript.start(["help"]) }
89
+ content.must =~ /thor zoo\s+# zoo around/m
90
+ ensure
91
+ $thor_runner = true
92
+ end
75
93
  end
76
94
 
77
95
  describe "when :for is supplied" do
@@ -79,6 +97,16 @@ describe Thor do
79
97
  capture(:stdout) { MyChildScript.start(["help"]) }.must =~ /animal KIND \s+# fish around/m
80
98
  end
81
99
  end
100
+
101
+ describe "when :hide is supplied" do
102
+ it "does not show the task in help" do
103
+ capture(:stdout) { MyScript.start(["help"]) }.must_not =~ /this is hidden/m
104
+ end
105
+
106
+ it "but the task is still invokcable not show the task in help" do
107
+ MyScript.start(["hidden", "yesyes"]).must == ["yesyes"]
108
+ end
109
+ end
82
110
  end
83
111
 
84
112
  describe "#method_options" do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thor
3
3
  version: !ruby/object:Gem::Version
4
- hash: 37
4
+ hash: 59
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 13
9
- - 7
10
- version: 0.13.7
9
+ - 8
10
+ version: 0.13.8
11
11
  platform: ruby
12
12
  authors:
13
13
  - Yehuda Katz
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-06-28 00:00:00 +02:00
19
+ date: 2010-07-16 00:00:00 +02:00
20
20
  default_executable:
21
21
  dependencies: []
22
22