thor 0.12.0 → 0.12.2

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.
@@ -25,10 +25,6 @@ class Thor
25
25
  self.options = other.options.dup if other.options
26
26
  end
27
27
 
28
- def short_description
29
- description.split("\n").first if description
30
- end
31
-
32
28
  # By default, a task invokes a method in the thor class. You can change this
33
29
  # implementation to create custom tasks.
34
30
  #
@@ -36,49 +32,51 @@ class Thor
36
32
  raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
37
33
  instance.send(name, *args)
38
34
  rescue ArgumentError => e
35
+ raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
39
36
  parse_argument_error(instance, e, caller)
40
37
  rescue NoMethodError => e
38
+ raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
41
39
  parse_no_method_error(instance, e)
42
40
  end
43
41
 
44
- # Returns the formatted usage. If a class is given, the class arguments are
45
- # injected in the usage.
46
- #
47
- def formatted_usage(klass=nil, namespace=false, show_options=true)
48
- formatted = if namespace.is_a?(String)
49
- "#{namespace}:"
50
- elsif klass && namespace
51
- "#{klass.namespace.gsub(/^default/,'')}:"
42
+ # Returns the formatted usage by injecting given required arguments
43
+ # and required options into the given usage.
44
+ def formatted_usage(klass, namespace=nil)
45
+ namespace = klass.namespace if namespace.nil?
46
+
47
+ # Add namespace
48
+ formatted = if namespace
49
+ "#{namespace.gsub(/^(default|thor:runner:)/,'')}:"
52
50
  else
53
51
  ""
54
52
  end
55
53
 
56
- formatted << formatted_arguments(klass)
57
- formatted << " #{formatted_options}" if show_options
58
- formatted.strip!
59
- formatted
60
- end
61
-
62
- # Injects the class arguments into the task usage.
63
- #
64
- def formatted_arguments(klass)
65
- if klass && !klass.arguments.empty?
54
+ # Add usage with required arguments
55
+ formatted << if klass && !klass.arguments.empty?
66
56
  usage.to_s.gsub(/^#{name}/) do |match|
67
- match << " " << klass.arguments.map{ |a| a.usage }.join(' ')
57
+ match << " " << required_arguments(klass)
68
58
  end
69
59
  else
70
60
  usage.to_s
71
61
  end
72
- end
73
62
 
74
- # Returns the options usage for this task.
75
- #
76
- def formatted_options
77
- @formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ")
63
+ # Add required options
64
+ formatted << " #{required_options}"
65
+
66
+ # Strip and go!
67
+ formatted.strip
78
68
  end
79
69
 
80
70
  protected
81
71
 
72
+ def required_arguments(klass)
73
+ klass.arguments.map{ |a| a.usage if a.required? }.compact.join(' ')
74
+ end
75
+
76
+ def required_options
77
+ @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
78
+ end
79
+
82
80
  # Given a target, checks if this class name is not a private/protected method.
83
81
  #
84
82
  def public_method?(instance) #:nodoc:
@@ -86,23 +84,15 @@ class Thor
86
84
  (collection & [name.to_s, name.to_sym]).empty?
87
85
  end
88
86
 
89
- # Clean everything that comes from the Thor gempath and remove the caller.
90
- #
91
- def sans_backtrace(backtrace, caller) #:nodoc:
92
- dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/
93
- saned = backtrace.reject { |frame| frame =~ dirname }
94
- saned -= caller
95
- end
96
-
97
87
  def parse_argument_error(instance, e, caller) #:nodoc:
98
- backtrace = sans_backtrace(e.backtrace, caller)
88
+ method_name = /`#{Regexp.escape(name.split(':').last)}'/
99
89
 
100
- if backtrace.empty? && e.message =~ /wrong number of arguments/
90
+ if e.message =~ /wrong number of arguments/ && e.backtrace.first.to_s =~ method_name
101
91
  if instance.is_a?(Thor::Group)
102
92
  raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
103
93
  else
104
94
  raise InvocationError, "'#{name}' was called incorrectly. Call as " <<
105
- "'#{formatted_usage(instance.class, true)}'"
95
+ "'#{formatted_usage(instance.class)}'"
106
96
  end
107
97
  else
108
98
  raise e
@@ -1,3 +1,3 @@
1
1
  class Thor
2
- VERSION = "0.12.0".freeze
2
+ VERSION = "0.12.2".freeze
3
3
  end
@@ -110,10 +110,11 @@ describe Thor::Actions::Directory do
110
110
  end
111
111
 
112
112
  it "yields a block" do
113
- invoke!("doc") do |f|
114
- %(doc/README doc/config.rb doc/rdoc.rb).must include(f)
115
- %(doc/components/).must_not include(f)
113
+ checked = false
114
+ invoke!("doc") do |content|
115
+ checked ||= !!(content =~ /FOO/)
116
116
  end
117
+ checked.must be_true
117
118
  end
118
119
  end
119
120
 
@@ -65,13 +65,19 @@ describe Thor::Actions do
65
65
  runner.inside("doc") do
66
66
  action :copy_file, "README"
67
67
  end
68
-
69
68
  exists_and_identical?("doc/README", "doc/README")
70
69
  end
71
70
 
72
71
  it "logs status" do
73
72
  action(:copy_file, "task.thor").must == " create task.thor\n"
74
73
  end
74
+
75
+ it "accepts a block to change output" do
76
+ action :copy_file, "task.thor" do |content|
77
+ "OMG" + content
78
+ end
79
+ File.read(File.join(destination_root, "task.thor")).must =~ /^OMG/
80
+ end
75
81
  end
76
82
 
77
83
  describe "#get" do
@@ -126,6 +132,13 @@ describe Thor::Actions do
126
132
  it "logs status" do
127
133
  capture(:stdout){ runner.template("doc/config.rb") }.must == " create doc/config.rb\n"
128
134
  end
135
+
136
+ it "accepts a block to change output" do
137
+ action :template, "doc/config.rb" do |content|
138
+ "OMG" + content
139
+ end
140
+ File.read(File.join(destination_root, "doc/config.rb")).must =~ /^OMG/
141
+ end
129
142
  end
130
143
 
131
144
  describe "when changing existent files" do
@@ -134,7 +134,7 @@ describe Thor::Base do
134
134
  it "allows extra options to be given" do
135
135
  hash = { "Foo" => B.class_options.values }
136
136
 
137
- content = capture(:stdout) { MyCounter.send(:class_options_help, Thor::Base.shell.new, nil, hash) }
137
+ content = capture(:stdout) { MyCounter.send(:class_options_help, Thor::Base.shell.new, hash) }
138
138
  content.must =~ /Foo options\:/
139
139
  content.must =~ /--last-name=LAST_NAME/
140
140
  end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'execute')
@@ -0,0 +1 @@
1
+ FOO = <%= "FOO" %>
@@ -0,0 +1,3 @@
1
+ __start__
2
+ README
3
+ __end__
@@ -0,0 +1,83 @@
1
+ class MyCounter < Thor::Group
2
+ include Thor::Actions
3
+ add_runtime_options!
4
+
5
+ def self.get_from_super
6
+ from_superclass(:get_from_super, 13)
7
+ end
8
+
9
+ def self.source_root
10
+ File.expand_path(File.dirname(__FILE__))
11
+ end
12
+ source_paths << File.expand_path("broken", File.dirname(__FILE__))
13
+
14
+ argument :first, :type => :numeric
15
+ argument :second, :type => :numeric, :default => 2
16
+
17
+ class_option :third, :type => :numeric, :desc => "The third argument", :default => 3,
18
+ :banner => "THREE", :aliases => "-t"
19
+ class_option :fourth, :type => :numeric, :desc => "The fourth argument"
20
+
21
+ desc <<-FOO
22
+ Description:
23
+ This generator run three tasks: one, two and three.
24
+ FOO
25
+
26
+ def one
27
+ first
28
+ end
29
+
30
+ def two
31
+ second
32
+ end
33
+
34
+ def three
35
+ options[:third]
36
+ end
37
+
38
+ def self.inherited(base)
39
+ super
40
+ base.source_paths.unshift(File.expand_path(File.join(File.dirname(__FILE__), "doc")))
41
+ end
42
+ end
43
+
44
+ class ClearCounter < MyCounter
45
+ remove_argument :first, :second, :undefine => true
46
+ remove_class_option :third
47
+
48
+ def self.source_root
49
+ File.expand_path(File.join(File.dirname(__FILE__), "bundle"))
50
+ end
51
+ end
52
+
53
+ class BrokenCounter < MyCounter
54
+ namespace "app:broken:counter"
55
+ class_option :fail, :type => :boolean, :default => false
56
+
57
+ class << self
58
+ undef_method :source_root
59
+ end
60
+
61
+ def one
62
+ options[:first]
63
+ end
64
+
65
+ def four
66
+ respond_to?(:fail)
67
+ end
68
+
69
+ def five
70
+ options[:fail] ? this_method_does_not_exist : 5
71
+ end
72
+ end
73
+
74
+ class WhinyGenerator < Thor::Group
75
+ include Thor::Actions
76
+
77
+ def self.source_root
78
+ File.expand_path(File.dirname(__FILE__))
79
+ end
80
+
81
+ def wrong_arity(required)
82
+ end
83
+ end
@@ -0,0 +1,112 @@
1
+ class A < Thor
2
+ include Thor::Actions
3
+
4
+ desc "one", "invoke one"
5
+ def one
6
+ p 1
7
+ invoke :two
8
+ invoke :three
9
+ end
10
+
11
+ desc "two", "invoke two"
12
+ def two
13
+ p 2
14
+ invoke :three
15
+ end
16
+
17
+ desc "three", "invoke three"
18
+ def three
19
+ p 3
20
+ end
21
+
22
+ desc "four", "invoke four"
23
+ def four
24
+ p 4
25
+ invoke "defined:five"
26
+ end
27
+
28
+ desc "five N", "check if number is equal 5"
29
+ def five(number)
30
+ number == 5
31
+ end
32
+
33
+ desc "invoker", "invoke a b task"
34
+ def invoker(*args)
35
+ invoke :b, :one, ["Jose"]
36
+ end
37
+ end
38
+
39
+ class B < Thor
40
+ class_option :last_name, :type => :string
41
+
42
+ desc "one FIRST_NAME", "invoke one"
43
+ def one(first_name)
44
+ "#{options.last_name}, #{first_name}"
45
+ end
46
+
47
+ desc "two", "invoke two"
48
+ def two
49
+ options
50
+ end
51
+
52
+ desc "three", "invoke three"
53
+ def three
54
+ self
55
+ end
56
+ end
57
+
58
+ class C < Thor::Group
59
+ include Thor::Actions
60
+
61
+ def one
62
+ p 1
63
+ end
64
+
65
+ def two
66
+ p 2
67
+ end
68
+
69
+ def three
70
+ p 3
71
+ end
72
+ end
73
+
74
+ class Defined < Thor::Group
75
+ class_option :unused, :type => :boolean, :desc => "This option has no use"
76
+
77
+ def one
78
+ p 1
79
+ invoke "a:two"
80
+ invoke "a:three"
81
+ invoke "a:four"
82
+ invoke "defined:five"
83
+ end
84
+
85
+ def five
86
+ p 5
87
+ end
88
+
89
+ def print_status
90
+ say_status :finished, :counting
91
+ end
92
+ end
93
+
94
+ class E < Thor::Group
95
+ invoke Defined
96
+ end
97
+
98
+ class F < Thor::Group
99
+ invoke "b:one" do |instance, klass, task|
100
+ instance.invoke klass, task, [ "Jose" ], :last_name => "Valim"
101
+ end
102
+ end
103
+
104
+ class G < Thor::Group
105
+ class_option :invoked, :type => :string, :default => "defined"
106
+ invoke_from_option :invoked
107
+ end
108
+
109
+ class H < Thor::Group
110
+ class_option :defined, :type => :boolean, :default => true
111
+ invoke_from_option :defined
112
+ end
@@ -0,0 +1,130 @@
1
+ class MyScript < Thor
2
+ group :script
3
+ default_task :example_default_task
4
+
5
+ map "-T" => :animal, ["-f", "--foo"] => :foo
6
+
7
+ desc "zoo", "zoo around"
8
+ def zoo
9
+ true
10
+ end
11
+
12
+ desc "animal TYPE", "horse around"
13
+
14
+ no_tasks do
15
+ def this_is_not_a_task
16
+ end
17
+ end
18
+
19
+ def animal(type)
20
+ [type]
21
+ end
22
+
23
+ desc "foo BAR", <<END
24
+ do some fooing
25
+ This is more info!
26
+ Everyone likes more info!
27
+ END
28
+ method_option :force, :type => :boolean, :desc => "Force to do some fooing"
29
+ def foo(bar)
30
+ [bar, options]
31
+ end
32
+
33
+ desc "example_default_task", "example!"
34
+ def example_default_task
35
+ options.empty? ? "default task" : options
36
+ end
37
+
38
+ desc "call_myself_with_wrong_arity", "get the right error"
39
+ def call_myself_with_wrong_arity
40
+ call_myself_with_wrong_arity(4)
41
+ end
42
+
43
+ desc "call_unexistent_method", "Call unexistent method inside a task"
44
+ def call_unexistent_method
45
+ boom!
46
+ end
47
+
48
+ desc "long_description", "a" * 80
49
+ def long_description
50
+ end
51
+
52
+ method_options :all => :boolean
53
+ desc "with_optional NAME", "invoke with optional name"
54
+ def with_optional(name=nil)
55
+ [ name, options ]
56
+ end
57
+
58
+ class AnotherScript < Thor
59
+ desc "baz", "do some bazing"
60
+ def baz
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def method_missing(meth, *args)
67
+ if meth == :boom!
68
+ super
69
+ else
70
+ [meth, args]
71
+ end
72
+ end
73
+
74
+ desc "what", "what"
75
+ def what
76
+ end
77
+ end
78
+
79
+ class MyChildScript < MyScript
80
+ remove_task :bar
81
+
82
+ method_options :force => :boolean, :param => :numeric
83
+ def initialize(*args)
84
+ super
85
+ end
86
+
87
+ desc "zoo", "zoo around"
88
+ method_options :param => :required
89
+ def zoo
90
+ options
91
+ end
92
+
93
+ desc "animal TYPE", "horse around"
94
+ def animal(type)
95
+ [type, options]
96
+ end
97
+ method_option :other, :type => :string, :default => "method default", :for => :animal
98
+ desc "animal KIND", "fish around", :for => :animal
99
+
100
+ desc "boom", "explodes everything"
101
+ def boom
102
+ end
103
+
104
+ remove_task :boom, :undefine => true
105
+ end
106
+
107
+ module Scripts
108
+ class MyScript < MyChildScript
109
+ argument :accessor, :type => :string
110
+ class_options :force => :boolean
111
+ method_option :new_option, :type => :string, :for => :example_default_task
112
+
113
+ def zoo
114
+ self.accessor
115
+ end
116
+ end
117
+
118
+ class MyDefaults < Thor
119
+ namespace :default
120
+ desc "test", "prints 'test'"
121
+ def test
122
+ puts "test"
123
+ end
124
+ end
125
+
126
+ class ChildDefault < Thor
127
+ namespace "default:child"
128
+ end
129
+ end
130
+