thor 0.16.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
data/bin/rake2thor DELETED
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'ruby2ruby'
4
- require 'parse_tree'
5
- if Ruby2Ruby::VERSION >= "1.2.0"
6
- require 'parse_tree_extensions'
7
- end
8
- require 'rake'
9
-
10
- input = ARGV[0] || 'Rakefile'
11
- output = ARGV[1] || 'Thorfile'
12
-
13
- $requires = []
14
-
15
- module Kernel
16
- def require_with_record(file)
17
- $requires << file if caller[1] =~ /rake2thor:/
18
- require_without_record file
19
- end
20
- alias_method :require_without_record, :require
21
- alias_method :require, :require_with_record
22
- end
23
-
24
- load input
25
-
26
- @private_methods = []
27
-
28
- def file_task_name(name)
29
- "compile_" + name.gsub('/', '_slash_').gsub('.', '_dot_').gsub(/\W/, '_')
30
- end
31
-
32
- def method_for_task(task)
33
- file_task = task.is_a?(Rake::FileTask)
34
- comment = task.instance_variable_get('@comment')
35
- prereqs = task.instance_variable_get('@prerequisites').select(&Rake::Task.method(:task_defined?))
36
- actions = task.instance_variable_get('@actions')
37
- name = task.name.gsub(/^([^:]+:)+/, '')
38
- name = file_task_name(name) if file_task
39
- meth = ''
40
-
41
- meth << "desc #{name.inspect}, #{comment.inspect}\n" if comment
42
- meth << "def #{name}\n"
43
-
44
- meth << prereqs.map do |pre|
45
- pre = pre.to_s
46
- pre = file_task_name(pre) if Rake::Task[pre].is_a?(Rake::FileTask)
47
- ' ' + pre
48
- end.join("\n")
49
-
50
- meth << "\n\n" unless prereqs.empty? || actions.empty?
51
-
52
- meth << actions.map do |act|
53
- act = act.to_ruby
54
- unless act.gsub!(/^proc \{ \|(\w+)\|\n/,
55
- " \\1 = Struct.new(:name).new(#{name.inspect}) # A crude mock Rake::Task object\n")
56
- act.gsub!(/^proc \{\n/, '')
57
- end
58
- act.gsub(/\n\}$/, '')
59
- end.join("\n")
60
-
61
- meth << "\nend"
62
-
63
- if file_task
64
- @private_methods << meth
65
- return
66
- end
67
-
68
- meth
69
- end
70
-
71
- body = Rake::Task.tasks.map(&method(:method_for_task)).compact.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
72
-
73
- unless @private_methods.empty?
74
- body << "\n\n private\n\n"
75
- body << @private_methods.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
76
- end
77
-
78
- requires = $requires.map { |r| "require #{r.inspect}" }.join("\n")
79
-
80
- File.open(output, 'w') { |f| f.write(<<END.lstrip) }
81
- #{requires}
82
-
83
- class Default < Thor
84
- #{body}
85
- end
86
- END
File without changes
@@ -1,9 +0,0 @@
1
- class File #:nodoc:
2
-
3
- unless File.respond_to?(:binread)
4
- def self.binread(file)
5
- File.open(file, 'rb') { |f| f.read }
6
- end
7
- end
8
-
9
- end
@@ -1,100 +0,0 @@
1
- class Thor
2
- module CoreExt #:nodoc:
3
-
4
- if RUBY_VERSION >= '1.9'
5
- class OrderedHash < ::Hash
6
- end
7
- else
8
- # This class is based on the Ruby 1.9 ordered hashes.
9
- #
10
- # It keeps the semantics and most of the efficiency of normal hashes
11
- # while also keeping track of the order in which elements were set.
12
- #
13
- class OrderedHash #:nodoc:
14
- include Enumerable
15
-
16
- Node = Struct.new(:key, :value, :next, :prev)
17
-
18
- def initialize
19
- @hash = {}
20
- end
21
-
22
- def [](key)
23
- @hash[key] && @hash[key].value
24
- end
25
-
26
- def []=(key, value)
27
- if node = @hash[key]
28
- node.value = value
29
- else
30
- node = Node.new(key, value)
31
-
32
- if @first.nil?
33
- @first = @last = node
34
- else
35
- node.prev = @last
36
- @last.next = node
37
- @last = node
38
- end
39
- end
40
-
41
- @hash[key] = node
42
- value
43
- end
44
-
45
- def delete(key)
46
- if node = @hash[key]
47
- prev_node = node.prev
48
- next_node = node.next
49
-
50
- next_node.prev = prev_node if next_node
51
- prev_node.next = next_node if prev_node
52
-
53
- @first = next_node if @first == node
54
- @last = prev_node if @last == node
55
-
56
- value = node.value
57
- end
58
-
59
- @hash.delete(key)
60
- value
61
- end
62
-
63
- def keys
64
- self.map { |k, v| k }
65
- end
66
-
67
- def values
68
- self.map { |k, v| v }
69
- end
70
-
71
- def each
72
- return unless @first
73
- yield [@first.key, @first.value]
74
- node = @first
75
- yield [node.key, node.value] while node = node.next
76
- self
77
- end
78
-
79
- def merge(other)
80
- hash = self.class.new
81
-
82
- self.each do |key, value|
83
- hash[key] = value
84
- end
85
-
86
- other.each do |key, value|
87
- hash[key] = value
88
- end
89
-
90
- hash
91
- end
92
-
93
- def empty?
94
- @hash.empty?
95
- end
96
- end
97
- end
98
-
99
- end
100
- end
data/lib/thor/task.rb DELETED
@@ -1,132 +0,0 @@
1
- class Thor
2
- class Task < Struct.new(:name, :description, :long_description, :usage, :options)
3
- FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
4
-
5
- def initialize(name, description, long_description, usage, options=nil)
6
- super(name.to_s, description, long_description, usage, options || {})
7
- end
8
-
9
- def initialize_copy(other) #:nodoc:
10
- super(other)
11
- self.options = other.options.dup if other.options
12
- end
13
-
14
- def hidden?
15
- false
16
- end
17
-
18
- # By default, a task invokes a method in the thor class. You can change this
19
- # implementation to create custom tasks.
20
- def run(instance, args=[])
21
- arity = nil
22
-
23
- if private_method?(instance)
24
- instance.class.handle_no_task_error(name)
25
- elsif public_method?(instance)
26
- arity = instance.method(name).arity
27
- instance.send(name, *args)
28
- elsif local_method?(instance, :method_missing)
29
- instance.send(:method_missing, name.to_sym, *args)
30
- else
31
- instance.class.handle_no_task_error(name)
32
- end
33
- rescue ArgumentError => e
34
- handle_argument_error?(instance, e, caller) ?
35
- instance.class.handle_argument_error(self, e, arity) : (raise e)
36
- rescue NoMethodError => e
37
- handle_no_method_error?(instance, e, caller) ?
38
- instance.class.handle_no_task_error(name) : (raise e)
39
- end
40
-
41
- # Returns the formatted usage by injecting given required arguments
42
- # and required options into the given usage.
43
- def formatted_usage(klass, namespace = true, subcommand = false)
44
- if namespace
45
- namespace = klass.namespace
46
- formatted = "#{namespace.gsub(/^(default)/,'')}:"
47
- end
48
- formatted = "#{klass.namespace.split(':').last} " if subcommand
49
-
50
- formatted ||= ""
51
-
52
- # Add usage with required arguments
53
- formatted << if klass && !klass.arguments.empty?
54
- usage.to_s.gsub(/^#{name}/) do |match|
55
- match << " " << klass.arguments.map{ |a| a.usage }.compact.join(' ')
56
- end
57
- else
58
- usage.to_s
59
- end
60
-
61
- # Add required options
62
- formatted << " #{required_options}"
63
-
64
- # Strip and go!
65
- formatted.strip
66
- end
67
-
68
- protected
69
-
70
- def not_debugging?(instance)
71
- !(instance.class.respond_to?(:debugging) && instance.class.debugging)
72
- end
73
-
74
- def required_options
75
- @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
76
- end
77
-
78
- # Given a target, checks if this class name is a public method.
79
- def public_method?(instance) #:nodoc:
80
- !(instance.public_methods & [name.to_s, name.to_sym]).empty?
81
- end
82
-
83
- def private_method?(instance)
84
- !(instance.private_methods & [name.to_s, name.to_sym]).empty?
85
- end
86
-
87
- def local_method?(instance, name)
88
- methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false)
89
- !(methods & [name.to_s, name.to_sym]).empty?
90
- end
91
-
92
- def sans_backtrace(backtrace, caller) #:nodoc:
93
- saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) }
94
- saned -= caller
95
- end
96
-
97
- def handle_argument_error?(instance, error, caller)
98
- not_debugging?(instance) && error.message =~ /wrong number of arguments/ && begin
99
- saned = sans_backtrace(error.backtrace, caller)
100
- # Ruby 1.9 always include the called method in the backtrace
101
- saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
102
- end
103
- end
104
-
105
- def handle_no_method_error?(instance, error, caller)
106
- not_debugging?(instance) &&
107
- error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
108
- end
109
- end
110
-
111
- # A task that is hidden in help messages but still invocable.
112
- class HiddenTask < Task
113
- def hidden?
114
- true
115
- end
116
- end
117
-
118
- # A dynamic task that handles method missing scenarios.
119
- class DynamicTask < Task
120
- def initialize(name, options=nil)
121
- super(name.to_s, "A dynamically-generated task", name.to_s, name.to_s, options)
122
- end
123
-
124
- def run(instance, args=[])
125
- if (instance.methods & [name.to_s, name.to_sym]).empty?
126
- super
127
- else
128
- instance.class.handle_no_task_error(name)
129
- end
130
- end
131
- end
132
- end
@@ -1,170 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
- require 'thor/actions'
3
-
4
- describe Thor::Actions::CreateFile do
5
- before do
6
- ::FileUtils.rm_rf(destination_root)
7
- end
8
-
9
- def create_file(destination=nil, config={}, options={})
10
- @base = MyCounter.new([1,2], options, { :destination_root => destination_root })
11
- @base.stub!(:file_name).and_return('rdoc')
12
-
13
- @action = Thor::Actions::CreateFile.new(@base, destination, "CONFIGURATION",
14
- { :verbose => !@silence }.merge(config))
15
- end
16
-
17
- def invoke!
18
- capture(:stdout){ @action.invoke! }
19
- end
20
-
21
- def revoke!
22
- capture(:stdout){ @action.revoke! }
23
- end
24
-
25
- def silence!
26
- @silence = true
27
- end
28
-
29
- describe "#invoke!" do
30
- it "creates a file" do
31
- create_file("doc/config.rb")
32
- invoke!
33
- File.exists?(File.join(destination_root, "doc/config.rb")).should be_true
34
- end
35
-
36
- it "does not create a file if pretending" do
37
- create_file("doc/config.rb", {}, :pretend => true)
38
- invoke!
39
- File.exists?(File.join(destination_root, "doc/config.rb")).should be_false
40
- end
41
-
42
- it "shows created status to the user" do
43
- create_file("doc/config.rb")
44
- invoke!.should == " create doc/config.rb\n"
45
- end
46
-
47
- it "does not show any information if log status is false" do
48
- silence!
49
- create_file("doc/config.rb")
50
- invoke!.should be_empty
51
- end
52
-
53
- it "returns the given destination" do
54
- capture(:stdout) do
55
- create_file("doc/config.rb").invoke!.should == "doc/config.rb"
56
- end
57
- end
58
-
59
- it "converts encoded instructions" do
60
- create_file("doc/%file_name%.rb.tt")
61
- invoke!
62
- File.exists?(File.join(destination_root, "doc/rdoc.rb.tt")).should be_true
63
- end
64
-
65
- describe "when file exists" do
66
- before do
67
- create_file("doc/config.rb")
68
- invoke!
69
- end
70
-
71
- describe "and is identical" do
72
- it "shows identical status" do
73
- create_file("doc/config.rb")
74
- invoke!
75
- invoke!.should == " identical doc/config.rb\n"
76
- end
77
- end
78
-
79
- describe "and is not identical" do
80
- before do
81
- File.open(File.join(destination_root, 'doc/config.rb'), 'w'){ |f| f.write("FOO = 3") }
82
- end
83
-
84
- it "shows forced status to the user if force is given" do
85
- create_file("doc/config.rb", {}, :force => true).should_not be_identical
86
- invoke!.should == " force doc/config.rb\n"
87
- end
88
-
89
- it "shows skipped status to the user if skip is given" do
90
- create_file("doc/config.rb", {}, :skip => true).should_not be_identical
91
- invoke!.should == " skip doc/config.rb\n"
92
- end
93
-
94
- it "shows forced status to the user if force is configured" do
95
- create_file("doc/config.rb", :force => true).should_not be_identical
96
- invoke!.should == " force doc/config.rb\n"
97
- end
98
-
99
- it "shows skipped status to the user if skip is configured" do
100
- create_file("doc/config.rb", :skip => true).should_not be_identical
101
- invoke!.should == " skip doc/config.rb\n"
102
- end
103
-
104
- it "shows conflict status to ther user" do
105
- create_file("doc/config.rb").should_not be_identical
106
- $stdin.should_receive(:gets).and_return('s')
107
- file = File.join(destination_root, 'doc/config.rb')
108
-
109
- content = invoke!
110
- content.should =~ /conflict doc\/config\.rb/
111
- content.should =~ /Overwrite #{file}\? \(enter "h" for help\) \[Ynaqdh\]/
112
- content.should =~ /skip doc\/config\.rb/
113
- end
114
-
115
- it "creates the file if the file collision menu returns true" do
116
- create_file("doc/config.rb")
117
- $stdin.should_receive(:gets).and_return('y')
118
- invoke!.should =~ /force doc\/config\.rb/
119
- end
120
-
121
- it "skips the file if the file collision menu returns false" do
122
- create_file("doc/config.rb")
123
- $stdin.should_receive(:gets).and_return('n')
124
- invoke!.should =~ /skip doc\/config\.rb/
125
- end
126
-
127
- it "executes the block given to show file content" do
128
- create_file("doc/config.rb")
129
- $stdin.should_receive(:gets).and_return('d')
130
- $stdin.should_receive(:gets).and_return('n')
131
- @base.shell.should_receive(:system).with(/diff -u/)
132
- invoke!
133
- end
134
- end
135
- end
136
- end
137
-
138
- describe "#revoke!" do
139
- it "removes the destination file" do
140
- create_file("doc/config.rb")
141
- invoke!
142
- revoke!
143
- File.exists?(@action.destination).should be_false
144
- end
145
-
146
- it "does not raise an error if the file does not exist" do
147
- create_file("doc/config.rb")
148
- revoke!
149
- File.exists?(@action.destination).should be_false
150
- end
151
- end
152
-
153
- describe "#exists?" do
154
- it "returns true if the destination file exists" do
155
- create_file("doc/config.rb")
156
- @action.exists?.should be_false
157
- invoke!
158
- @action.exists?.should be_true
159
- end
160
- end
161
-
162
- describe "#identical?" do
163
- it "returns true if the destination file and is identical" do
164
- create_file("doc/config.rb")
165
- @action.identical?.should be_false
166
- invoke!
167
- @action.identical?.should be_true
168
- end
169
- end
170
- end
@@ -1,81 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
- require 'thor/actions'
3
- require 'tempfile'
4
-
5
- describe Thor::Actions::CreateLink do
6
- before do
7
- @hardlink_to = File.join(Dir.tmpdir, 'linkdest.rb')
8
- ::FileUtils.rm_rf(destination_root)
9
- ::FileUtils.rm_rf(@hardlink_to)
10
- end
11
-
12
- def create_link(destination=nil, config={}, options={})
13
- @base = MyCounter.new([1,2], options, { :destination_root => destination_root })
14
- @base.stub!(:file_name).and_return('rdoc')
15
-
16
- @tempfile = Tempfile.new("config.rb")
17
-
18
- @action = Thor::Actions::CreateLink.new(@base, destination, @tempfile.path,
19
- { :verbose => !@silence }.merge(config))
20
- end
21
-
22
- def invoke!
23
- capture(:stdout){ @action.invoke! }
24
- end
25
-
26
- def silence!
27
- @silence = true
28
- end
29
-
30
- describe "#invoke!" do
31
- it "creates a symbolic link for :symbolic => true" do
32
- create_link("doc/config.rb", :symbolic => true)
33
- invoke!
34
- destination_path = File.join(destination_root, "doc/config.rb")
35
- File.exists?(destination_path).should be_true
36
- File.symlink?(destination_path).should be_true
37
- end
38
-
39
- it "creates a hard link for :symbolic => false" do
40
- create_link(@hardlink_to, :symbolic => false)
41
- invoke!
42
- destination_path = @hardlink_to
43
- File.exists?(destination_path).should be_true
44
- File.symlink?(destination_path).should be_false
45
- end
46
-
47
- it "creates a symbolic link by default" do
48
- create_link("doc/config.rb")
49
- invoke!
50
- destination_path = File.join(destination_root, "doc/config.rb")
51
- File.exists?(destination_path).should be_true
52
- File.symlink?(destination_path).should be_true
53
- end
54
-
55
- it "does not create a link if pretending" do
56
- create_link("doc/config.rb", {}, :pretend => true)
57
- invoke!
58
- File.exists?(File.join(destination_root, "doc/config.rb")).should be_false
59
- end
60
-
61
- it "shows created status to the user" do
62
- create_link("doc/config.rb")
63
- invoke!.should == " create doc/config.rb\n"
64
- end
65
-
66
- it "does not show any information if log status is false" do
67
- silence!
68
- create_link("doc/config.rb")
69
- invoke!.should be_empty
70
- end
71
- end
72
-
73
- describe "#identical?" do
74
- it "returns true if the destination link exists and is identical" do
75
- create_link("doc/config.rb")
76
- @action.identical?.should be_false
77
- invoke!
78
- @action.identical?.should be_true
79
- end
80
- end
81
- end