millisami-thor 0.14.6

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.
Files changed (82) hide show
  1. data/.autotest +8 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +1 -0
  5. data/CHANGELOG.rdoc +103 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE.md +20 -0
  8. data/README.md +26 -0
  9. data/Thorfile +13 -0
  10. data/bin/rake2thor +86 -0
  11. data/bin/thor +6 -0
  12. data/lib/thor/actions/create_file.rb +105 -0
  13. data/lib/thor/actions/create_link.rb +57 -0
  14. data/lib/thor/actions/directory.rb +93 -0
  15. data/lib/thor/actions/empty_directory.rb +134 -0
  16. data/lib/thor/actions/file_manipulation.rb +270 -0
  17. data/lib/thor/actions/inject_into_file.rb +109 -0
  18. data/lib/thor/actions.rb +314 -0
  19. data/lib/thor/base.rb +598 -0
  20. data/lib/thor/core_ext/file_binary_read.rb +9 -0
  21. data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  22. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  23. data/lib/thor/error.rb +30 -0
  24. data/lib/thor/group.rb +276 -0
  25. data/lib/thor/invocation.rb +168 -0
  26. data/lib/thor/parser/argument.rb +67 -0
  27. data/lib/thor/parser/arguments.rb +165 -0
  28. data/lib/thor/parser/option.rb +120 -0
  29. data/lib/thor/parser/options.rb +181 -0
  30. data/lib/thor/parser.rb +4 -0
  31. data/lib/thor/rake_compat.rb +70 -0
  32. data/lib/thor/runner.rb +309 -0
  33. data/lib/thor/shell/basic.rb +302 -0
  34. data/lib/thor/shell/color.rb +108 -0
  35. data/lib/thor/shell/html.rb +121 -0
  36. data/lib/thor/shell.rb +88 -0
  37. data/lib/thor/task.rb +129 -0
  38. data/lib/thor/util.rb +229 -0
  39. data/lib/thor/version.rb +3 -0
  40. data/lib/thor.rb +336 -0
  41. data/spec/actions/create_file_spec.rb +170 -0
  42. data/spec/actions/directory_spec.rb +136 -0
  43. data/spec/actions/empty_directory_spec.rb +98 -0
  44. data/spec/actions/file_manipulation_spec.rb +317 -0
  45. data/spec/actions/inject_into_file_spec.rb +135 -0
  46. data/spec/actions_spec.rb +322 -0
  47. data/spec/base_spec.rb +274 -0
  48. data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  49. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  50. data/spec/fixtures/application.rb +2 -0
  51. data/spec/fixtures/bundle/execute.rb +6 -0
  52. data/spec/fixtures/bundle/main.thor +1 -0
  53. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  54. data/spec/fixtures/doc/README +3 -0
  55. data/spec/fixtures/doc/block_helper.rb +3 -0
  56. data/spec/fixtures/doc/components/.empty_directory +0 -0
  57. data/spec/fixtures/doc/config.rb +1 -0
  58. data/spec/fixtures/doc/config.yaml.tt +1 -0
  59. data/spec/fixtures/group.thor +114 -0
  60. data/spec/fixtures/invoke.thor +112 -0
  61. data/spec/fixtures/path with spaces +0 -0
  62. data/spec/fixtures/script.thor +184 -0
  63. data/spec/fixtures/task.thor +10 -0
  64. data/spec/group_spec.rb +216 -0
  65. data/spec/invocation_spec.rb +100 -0
  66. data/spec/parser/argument_spec.rb +47 -0
  67. data/spec/parser/arguments_spec.rb +65 -0
  68. data/spec/parser/option_spec.rb +202 -0
  69. data/spec/parser/options_spec.rb +329 -0
  70. data/spec/rake_compat_spec.rb +72 -0
  71. data/spec/register_spec.rb +92 -0
  72. data/spec/runner_spec.rb +210 -0
  73. data/spec/shell/basic_spec.rb +223 -0
  74. data/spec/shell/color_spec.rb +41 -0
  75. data/spec/shell/html_spec.rb +27 -0
  76. data/spec/shell_spec.rb +47 -0
  77. data/spec/spec_helper.rb +54 -0
  78. data/spec/task_spec.rb +74 -0
  79. data/spec/thor_spec.rb +362 -0
  80. data/spec/util_spec.rb +163 -0
  81. data/thor.gemspec +25 -0
  82. metadata +241 -0
@@ -0,0 +1,270 @@
1
+ require 'erb'
2
+ require 'open-uri'
3
+
4
+ class Thor
5
+ module Actions
6
+
7
+ # Copies the file from the relative source to the relative destination. If
8
+ # the destination is not given it's assumed to be equal to the source.
9
+ #
10
+ # ==== Parameters
11
+ # source<String>:: the relative path to the source root.
12
+ # destination<String>:: the relative path to the destination root.
13
+ # config<Hash>:: give :verbose => false to not log the status.
14
+ #
15
+ # ==== Examples
16
+ #
17
+ # copy_file "README", "doc/README"
18
+ #
19
+ # copy_file "doc/README"
20
+ #
21
+ def copy_file(source, *args, &block)
22
+ config = args.last.is_a?(Hash) ? args.pop : {}
23
+ destination = args.first || source
24
+ source = File.expand_path(find_in_source_paths(source.to_s))
25
+
26
+ create_file destination, nil, config do
27
+ content = File.binread(source)
28
+ content = block.call(content) if block
29
+ content
30
+ end
31
+ end
32
+
33
+ # Links the file from the relative source to the relative destination. If
34
+ # the destination is not given it's assumed to be equal to the source.
35
+ #
36
+ # ==== Parameters
37
+ # source<String>:: the relative path to the source root.
38
+ # destination<String>:: the relative path to the destination root.
39
+ # config<Hash>:: give :verbose => false to not log the status.
40
+ #
41
+ # ==== Examples
42
+ #
43
+ # link_file "README", "doc/README"
44
+ #
45
+ # link_file "doc/README"
46
+ #
47
+ def link_file(source, *args, &block)
48
+ config = args.last.is_a?(Hash) ? args.pop : {}
49
+ destination = args.first || source
50
+ source = File.expand_path(find_in_source_paths(source.to_s))
51
+
52
+ create_link destination, source, config
53
+ end
54
+
55
+ # Gets the content at the given address and places it at the given relative
56
+ # destination. If a block is given instead of destination, the content of
57
+ # the url is yielded and used as location.
58
+ #
59
+ # ==== Parameters
60
+ # source<String>:: the address of the given content.
61
+ # destination<String>:: the relative path to the destination root.
62
+ # config<Hash>:: give :verbose => false to not log the status.
63
+ #
64
+ # ==== Examples
65
+ #
66
+ # get "http://gist.github.com/103208", "doc/README"
67
+ #
68
+ # get "http://gist.github.com/103208" do |content|
69
+ # content.split("\n").first
70
+ # end
71
+ #
72
+ def get(source, *args, &block)
73
+ config = args.last.is_a?(Hash) ? args.pop : {}
74
+ destination = args.first
75
+
76
+ source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^https?\:\/\//
77
+ render = open(source) {|input| input.binmode.read }
78
+
79
+ destination ||= if block_given?
80
+ block.arity == 1 ? block.call(render) : block.call
81
+ else
82
+ File.basename(source)
83
+ end
84
+
85
+ create_file destination, render, config
86
+ end
87
+
88
+ # Gets an ERB template at the relative source, executes it and makes a copy
89
+ # at the relative destination. If the destination is not given it's assumed
90
+ # to be equal to the source removing .tt from the filename.
91
+ #
92
+ # ==== Parameters
93
+ # source<String>:: the relative path to the source root.
94
+ # destination<String>:: the relative path to the destination root.
95
+ # config<Hash>:: give :verbose => false to not log the status.
96
+ #
97
+ # ==== Examples
98
+ #
99
+ # template "README", "doc/README"
100
+ #
101
+ # template "doc/README"
102
+ #
103
+ def template(source, *args, &block)
104
+ config = args.last.is_a?(Hash) ? args.pop : {}
105
+ destination = args.first || source.sub(/\.tt$/, '')
106
+
107
+ source = File.expand_path(find_in_source_paths(source.to_s))
108
+ context = instance_eval('binding')
109
+
110
+ create_file destination, nil, config do
111
+ content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context)
112
+ content = block.call(content) if block
113
+ content
114
+ end
115
+ end
116
+
117
+ # Changes the mode of the given file or directory.
118
+ #
119
+ # ==== Parameters
120
+ # mode<Integer>:: the file mode
121
+ # path<String>:: the name of the file to change mode
122
+ # config<Hash>:: give :verbose => false to not log the status.
123
+ #
124
+ # ==== Example
125
+ #
126
+ # chmod "script/*", 0755
127
+ #
128
+ def chmod(path, mode, config={})
129
+ return unless behavior == :invoke
130
+ path = File.expand_path(path, destination_root)
131
+ say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
132
+ FileUtils.chmod_R(mode, path) unless options[:pretend]
133
+ end
134
+
135
+ # Prepend text to a file. Since it depends on insert_into_file, it's reversible.
136
+ #
137
+ # ==== Parameters
138
+ # path<String>:: path of the file to be changed
139
+ # data<String>:: the data to prepend to the file, can be also given as a block.
140
+ # config<Hash>:: give :verbose => false to not log the status.
141
+ #
142
+ # ==== Example
143
+ #
144
+ # prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"'
145
+ #
146
+ # prepend_to_file 'config/environments/test.rb' do
147
+ # 'config.gem "rspec"'
148
+ # end
149
+ #
150
+ def prepend_to_file(path, *args, &block)
151
+ config = args.last.is_a?(Hash) ? args.pop : {}
152
+ config.merge!(:after => /\A/)
153
+ insert_into_file(path, *(args << config), &block)
154
+ end
155
+ alias_method :prepend_file, :prepend_to_file
156
+
157
+ # Append text to a file. Since it depends on insert_into_file, it's reversible.
158
+ #
159
+ # ==== Parameters
160
+ # path<String>:: path of the file to be changed
161
+ # data<String>:: the data to append to the file, can be also given as a block.
162
+ # config<Hash>:: give :verbose => false to not log the status.
163
+ #
164
+ # ==== Example
165
+ #
166
+ # append_to_file 'config/environments/test.rb', 'config.gem "rspec"'
167
+ #
168
+ # append_to_file 'config/environments/test.rb' do
169
+ # 'config.gem "rspec"'
170
+ # end
171
+ #
172
+ def append_to_file(path, *args, &block)
173
+ config = args.last.is_a?(Hash) ? args.pop : {}
174
+ config.merge!(:before => /\z/)
175
+ insert_into_file(path, *(args << config), &block)
176
+ end
177
+ alias_method :append_file, :append_to_file
178
+
179
+ # Injects text right after the class definition. Since it depends on
180
+ # insert_into_file, it's reversible.
181
+ #
182
+ # ==== Parameters
183
+ # path<String>:: path of the file to be changed
184
+ # klass<String|Class>:: the class to be manipulated
185
+ # data<String>:: the data to append to the class, can be also given as a block.
186
+ # config<Hash>:: give :verbose => false to not log the status.
187
+ #
188
+ # ==== Examples
189
+ #
190
+ # inject_into_class "app/controllers/application_controller.rb", " filter_parameter :password\n"
191
+ #
192
+ # inject_into_class "app/controllers/application_controller.rb", ApplicationController do
193
+ # " filter_parameter :password\n"
194
+ # end
195
+ #
196
+ def inject_into_class(path, klass, *args, &block)
197
+ config = args.last.is_a?(Hash) ? args.pop : {}
198
+ config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/)
199
+ insert_into_file(path, *(args << config), &block)
200
+ end
201
+
202
+ # Run a regular expression replacement on a file.
203
+ #
204
+ # ==== Parameters
205
+ # path<String>:: path of the file to be changed
206
+ # flag<Regexp|String>:: the regexp or string to be replaced
207
+ # replacement<String>:: the replacement, can be also given as a block
208
+ # config<Hash>:: give :verbose => false to not log the status.
209
+ #
210
+ # ==== Example
211
+ #
212
+ # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
213
+ #
214
+ # gsub_file 'README', /rake/, :green do |match|
215
+ # match << " no more. Use thor!"
216
+ # end
217
+ #
218
+ def gsub_file(path, flag, *args, &block)
219
+ return unless behavior == :invoke
220
+ config = args.last.is_a?(Hash) ? args.pop : {}
221
+
222
+ path = File.expand_path(path, destination_root)
223
+ say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
224
+
225
+ unless options[:pretend]
226
+ content = File.binread(path)
227
+ content.gsub!(flag, *args, &block)
228
+ File.open(path, 'wb') { |file| file.write(content) }
229
+ end
230
+ end
231
+
232
+ # Removes a file at the given location.
233
+ #
234
+ # ==== Parameters
235
+ # path<String>:: path of the file to be changed
236
+ # config<Hash>:: give :verbose => false to not log the status.
237
+ #
238
+ # ==== Example
239
+ #
240
+ # remove_file 'README'
241
+ # remove_file 'app/controllers/application_controller.rb'
242
+ #
243
+ def remove_file(path, config={})
244
+ return unless behavior == :invoke
245
+ path = File.expand_path(path, destination_root)
246
+
247
+ say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
248
+ ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path)
249
+ end
250
+ alias :remove_dir :remove_file
251
+
252
+ private
253
+ attr_accessor :output_buffer
254
+ def concat(string)
255
+ @output_buffer.concat(string)
256
+ end
257
+
258
+ def capture(*args, &block)
259
+ with_output_buffer { block.call(*args) }
260
+ end
261
+
262
+ def with_output_buffer(buf = '') #:nodoc:
263
+ self.output_buffer, old_buffer = buf, output_buffer
264
+ yield
265
+ output_buffer
266
+ ensure
267
+ self.output_buffer = old_buffer
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,109 @@
1
+ require 'thor/actions/empty_directory'
2
+
3
+ class Thor
4
+ module Actions
5
+
6
+ # Injects the given content into a file. Different from gsub_file, this
7
+ # method is reversible.
8
+ #
9
+ # ==== Parameters
10
+ # destination<String>:: Relative path to the destination root
11
+ # data<String>:: Data to add to the file. Can be given as a block.
12
+ # config<Hash>:: give :verbose => false to not log the status and the flag
13
+ # for injection (:after or :before) or :force => true for
14
+ # insert two or more times the same content.
15
+ #
16
+ # ==== Examples
17
+ #
18
+ # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"
19
+ #
20
+ # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
21
+ # gems = ask "Which gems would you like to add?"
22
+ # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
23
+ # end
24
+ #
25
+ def insert_into_file(destination, *args, &block)
26
+ if block_given?
27
+ data, config = block, args.shift
28
+ else
29
+ data, config = args.shift, args.shift
30
+ end
31
+ action InjectIntoFile.new(self, destination, data, config)
32
+ end
33
+ alias_method :inject_into_file, :insert_into_file
34
+
35
+ class InjectIntoFile < EmptyDirectory #:nodoc:
36
+ attr_reader :replacement, :flag, :behavior
37
+
38
+ def initialize(base, destination, data, config)
39
+ super(base, destination, { :verbose => true }.merge(config))
40
+
41
+ @behavior, @flag = if @config.key?(:after)
42
+ [:after, @config.delete(:after)]
43
+ else
44
+ [:before, @config.delete(:before)]
45
+ end
46
+
47
+ @replacement = data.is_a?(Proc) ? data.call : data
48
+ @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp)
49
+ end
50
+
51
+ def invoke!
52
+ say_status :invoke
53
+
54
+ content = if @behavior == :after
55
+ '\0' + replacement
56
+ else
57
+ replacement + '\0'
58
+ end
59
+
60
+ replace!(/#{flag}/, content, config[:force])
61
+ end
62
+
63
+ def revoke!
64
+ say_status :revoke
65
+
66
+ regexp = if @behavior == :after
67
+ content = '\1\2'
68
+ /(#{flag})(.*)(#{Regexp.escape(replacement)})/m
69
+ else
70
+ content = '\2\3'
71
+ /(#{Regexp.escape(replacement)})(.*)(#{flag})/m
72
+ end
73
+
74
+ replace!(regexp, content, true)
75
+ end
76
+
77
+ protected
78
+
79
+ def say_status(behavior)
80
+ status = if behavior == :invoke
81
+ if flag == /\A/
82
+ :prepend
83
+ elsif flag == /\z/
84
+ :append
85
+ else
86
+ :insert
87
+ end
88
+ else
89
+ :subtract
90
+ end
91
+
92
+ super(status, config[:verbose])
93
+ end
94
+
95
+ # Adds the content to the file.
96
+ #
97
+ def replace!(regexp, string, force)
98
+ unless base.options[:pretend]
99
+ content = File.binread(destination)
100
+ if force || !content.include?(replacement)
101
+ content.gsub!(regexp, string)
102
+ File.open(destination, 'wb') { |file| file.write(content) }
103
+ end
104
+ end
105
+ end
106
+
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,314 @@
1
+ require 'fileutils'
2
+ require 'uri'
3
+ require 'thor/core_ext/file_binary_read'
4
+ require 'thor/actions/create_file'
5
+ require 'thor/actions/create_link'
6
+ require 'thor/actions/directory'
7
+ require 'thor/actions/empty_directory'
8
+ require 'thor/actions/file_manipulation'
9
+ require 'thor/actions/inject_into_file'
10
+
11
+ class Thor
12
+ module Actions
13
+ attr_accessor :behavior
14
+
15
+ def self.included(base) #:nodoc:
16
+ base.extend ClassMethods
17
+ end
18
+
19
+ module ClassMethods
20
+ # Hold source paths for one Thor instance. source_paths_for_search is the
21
+ # method responsible to gather source_paths from this current class,
22
+ # inherited paths and the source root.
23
+ #
24
+ def source_paths
25
+ @_source_paths ||= []
26
+ end
27
+
28
+ # Stores and return the source root for this class
29
+ def source_root(path=nil)
30
+ @_source_root = path if path
31
+ @_source_root
32
+ end
33
+
34
+ # Returns the source paths in the following order:
35
+ #
36
+ # 1) This class source paths
37
+ # 2) Source root
38
+ # 3) Parents source paths
39
+ #
40
+ def source_paths_for_search
41
+ paths = []
42
+ paths += self.source_paths
43
+ paths << self.source_root if self.source_root
44
+ paths += from_superclass(:source_paths, [])
45
+ paths
46
+ end
47
+
48
+ # Add runtime options that help actions execution.
49
+ #
50
+ def add_runtime_options!
51
+ class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
52
+ :desc => "Overwrite files that already exist"
53
+
54
+ class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
55
+ :desc => "Run but do not make any changes"
56
+
57
+ class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
58
+ :desc => "Supress status output"
59
+
60
+ class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
61
+ :desc => "Skip files that already exist"
62
+ end
63
+ end
64
+
65
+ # Extends initializer to add more configuration options.
66
+ #
67
+ # ==== Configuration
68
+ # behavior<Symbol>:: The actions default behavior. Can be :invoke or :revoke.
69
+ # It also accepts :force, :skip and :pretend to set the behavior
70
+ # and the respective option.
71
+ #
72
+ # destination_root<String>:: The root directory needed for some actions.
73
+ #
74
+ def initialize(args=[], options={}, config={})
75
+ self.behavior = case config[:behavior].to_s
76
+ when "force", "skip"
77
+ _cleanup_options_and_set(options, config[:behavior])
78
+ :invoke
79
+ when "revoke"
80
+ :revoke
81
+ else
82
+ :invoke
83
+ end
84
+
85
+ super
86
+ self.destination_root = config[:destination_root]
87
+ end
88
+
89
+ # Wraps an action object and call it accordingly to the thor class behavior.
90
+ #
91
+ def action(instance) #:nodoc:
92
+ if behavior == :revoke
93
+ instance.revoke!
94
+ else
95
+ instance.invoke!
96
+ end
97
+ end
98
+
99
+ # Returns the root for this thor class (also aliased as destination root).
100
+ #
101
+ def destination_root
102
+ @destination_stack.last
103
+ end
104
+
105
+ # Sets the root for this thor class. Relatives path are added to the
106
+ # directory where the script was invoked and expanded.
107
+ #
108
+ def destination_root=(root)
109
+ @destination_stack ||= []
110
+ @destination_stack[0] = File.expand_path(root || '')
111
+ end
112
+
113
+ # Returns the given path relative to the absolute root (ie, root where
114
+ # the script started).
115
+ #
116
+ def relative_to_original_destination_root(path, remove_dot=true)
117
+ path = path.gsub(@destination_stack[0], '.')
118
+ remove_dot ? (path[2..-1] || '') : path
119
+ end
120
+
121
+ # Holds source paths in instance so they can be manipulated.
122
+ #
123
+ def source_paths
124
+ @source_paths ||= self.class.source_paths_for_search
125
+ end
126
+
127
+ # Receives a file or directory and search for it in the source paths.
128
+ #
129
+ def find_in_source_paths(file)
130
+ relative_root = relative_to_original_destination_root(destination_root, false)
131
+
132
+ source_paths.each do |source|
133
+ source_file = File.expand_path(file, File.join(source, relative_root))
134
+ return source_file if File.exists?(source_file)
135
+ end
136
+
137
+ message = "Could not find #{file.inspect} in any of your source paths. "
138
+
139
+ unless self.class.source_root
140
+ message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. "
141
+ end
142
+
143
+ if source_paths.empty?
144
+ message << "Currently you have no source paths."
145
+ else
146
+ message << "Your current source paths are: \n#{source_paths.join("\n")}"
147
+ end
148
+
149
+ raise Error, message
150
+ end
151
+
152
+ # Do something in the root or on a provided subfolder. If a relative path
153
+ # is given it's referenced from the current root. The full path is yielded
154
+ # to the block you provide. The path is set back to the previous path when
155
+ # the method exits.
156
+ #
157
+ # ==== Parameters
158
+ # dir<String>:: the directory to move to.
159
+ # config<Hash>:: give :verbose => true to log and use padding.
160
+ #
161
+ def inside(dir='', config={}, &block)
162
+ verbose = config.fetch(:verbose, false)
163
+ pretend = options[:pretend]
164
+
165
+ say_status :inside, dir, verbose
166
+ shell.padding += 1 if verbose
167
+ @destination_stack.push File.expand_path(dir, destination_root)
168
+
169
+ # If the directory doesnt exist and we're not pretending
170
+ if !File.exist?(destination_root) && !pretend
171
+ FileUtils.mkdir_p(destination_root)
172
+ end
173
+
174
+ if pretend
175
+ # In pretend mode, just yield down to the block
176
+ block.arity == 1 ? yield(destination_root) : yield
177
+ else
178
+ FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
179
+ end
180
+
181
+ @destination_stack.pop
182
+ shell.padding -= 1 if verbose
183
+ end
184
+
185
+ # Goes to the root and execute the given block.
186
+ #
187
+ def in_root
188
+ inside(@destination_stack.first) { yield }
189
+ end
190
+
191
+ # Loads an external file and execute it in the instance binding.
192
+ #
193
+ # ==== Parameters
194
+ # path<String>:: The path to the file to execute. Can be a web address or
195
+ # a relative path from the source root.
196
+ #
197
+ # ==== Examples
198
+ #
199
+ # apply "http://gist.github.com/103208"
200
+ #
201
+ # apply "recipes/jquery.rb"
202
+ #
203
+ def apply(path, config={})
204
+ verbose = config.fetch(:verbose, true)
205
+ is_uri = path =~ /^https?\:\/\//
206
+ path = find_in_source_paths(path) unless is_uri
207
+
208
+ say_status :apply, path, verbose
209
+ shell.padding += 1 if verbose
210
+
211
+ if is_uri
212
+ contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read }
213
+ else
214
+ contents = open(path) {|io| io.read }
215
+ end
216
+
217
+ instance_eval(contents, path)
218
+ shell.padding -= 1 if verbose
219
+ end
220
+
221
+ # Executes a command returning the contents of the command.
222
+ #
223
+ # ==== Parameters
224
+ # command<String>:: the command to be executed.
225
+ # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with
226
+ # to append an executable to command executation.
227
+ #
228
+ # ==== Example
229
+ #
230
+ # inside('vendor') do
231
+ # run_cmd('ln -s ~/edge rails')
232
+ # end
233
+ #
234
+ def run_cmd(command, config={})
235
+ return unless behavior == :invoke
236
+
237
+ destination = relative_to_original_destination_root(destination_root, false)
238
+ desc = "#{command} from #{destination.inspect}"
239
+
240
+ if config[:with]
241
+ desc = "#{File.basename(config[:with].to_s)} #{desc}"
242
+ command = "#{config[:with]} #{command}"
243
+ end
244
+
245
+ say_status :run_cmd, desc, config.fetch(:verbose, true)
246
+
247
+ unless options[:pretend]
248
+ config[:capture] ? `#{command}` : system("#{command}")
249
+ end
250
+ end
251
+
252
+ # Executes a ruby script (taking into account WIN32 platform quirks).
253
+ #
254
+ # ==== Parameters
255
+ # command<String>:: the command to be executed.
256
+ # config<Hash>:: give :verbose => false to not log the status.
257
+ #
258
+ def run_ruby_script(command, config={})
259
+ return unless behavior == :invoke
260
+ run_cmd command, config.merge(:with => Thor::Util.ruby_command)
261
+ end
262
+
263
+ # Run a thor command. A hash of options can be given and it's converted to
264
+ # switches.
265
+ #
266
+ # ==== Parameters
267
+ # task<String>:: the task to be invoked
268
+ # args<Array>:: arguments to the task
269
+ # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output.
270
+ # Other options are given as parameter to Thor.
271
+ #
272
+ #
273
+ # ==== Examples
274
+ #
275
+ # thor :install, "http://gist.github.com/103208"
276
+ # #=> thor install http://gist.github.com/103208
277
+ #
278
+ # thor :list, :all => true, :substring => 'rails'
279
+ # #=> thor list --all --substring=rails
280
+ #
281
+ def thor(task, *args)
282
+ config = args.last.is_a?(Hash) ? args.pop : {}
283
+ verbose = config.key?(:verbose) ? config.delete(:verbose) : true
284
+ pretend = config.key?(:pretend) ? config.delete(:pretend) : false
285
+ capture = config.key?(:capture) ? config.delete(:capture) : false
286
+
287
+ args.unshift task
288
+ args.push Thor::Options.to_switches(config)
289
+ command = args.join(' ').strip
290
+
291
+ run_cmd command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture
292
+ end
293
+
294
+ protected
295
+
296
+ # Allow current root to be shared between invocations.
297
+ #
298
+ def _shared_configuration #:nodoc:
299
+ super.merge!(:destination_root => self.destination_root)
300
+ end
301
+
302
+ def _cleanup_options_and_set(options, key) #:nodoc:
303
+ case options
304
+ when Array
305
+ %w(--force -f --skip -s).each { |i| options.delete(i) }
306
+ options << "--#{key}"
307
+ when Hash
308
+ [:force, :skip, "force", "skip"].each { |i| options.delete(i) }
309
+ options.merge!(key => true)
310
+ end
311
+ end
312
+
313
+ end
314
+ end