thor 0.12.0 → 0.12.2

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