thor 0.13.7 → 0.13.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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