3b49b1fa92531d4ce019c18a86081b6e1ef18b77
[feedcatcher.git] / vendor / rails / railties / lib / rails_generator / generators / applications / app / template_runner.rb
1 require File.dirname(__FILE__) + '/scm/scm'
2 require File.dirname(__FILE__) + '/scm/git'
3 require File.dirname(__FILE__) + '/scm/svn'
4
5 require 'open-uri'
6 require 'fileutils'
7
8 module Rails
9 class TemplateRunner
10 attr_reader :root
11 attr_writer :logger
12
13 def initialize(template, root = '') # :nodoc:
14 @root = File.expand_path(File.directory?(root) ? root : File.join(Dir.pwd, root))
15
16 log 'applying', "template: #{template}"
17
18 load_template(template)
19
20 log 'applied', "#{template}"
21 end
22
23 def load_template(template)
24 begin
25 code = open(template).read
26 in_root { self.instance_eval(code) }
27 rescue LoadError, Errno::ENOENT => e
28 raise "The template [#{template}] could not be loaded. Error: #{e}"
29 end
30 end
31
32 # Create a new file in the Rails project folder. Specify the
33 # relative path from RAILS_ROOT. Data is the return value of a block
34 # or a data string.
35 #
36 # ==== Examples
37 #
38 # file("lib/fun_party.rb") do
39 # hostname = ask("What is the virtual hostname I should use?")
40 # "vhost.name = #{hostname}"
41 # end
42 #
43 # file("config/apach.conf", "your apache config")
44 #
45 def file(filename, data = nil, log_action = true, &block)
46 log 'file', filename if log_action
47 dir, file = [File.dirname(filename), File.basename(filename)]
48
49 inside(dir) do
50 File.open(file, "w") do |f|
51 if block_given?
52 f.write(block.call)
53 else
54 f.write(data)
55 end
56 end
57 end
58 end
59
60 # Install a plugin. You must provide either a Subversion url or Git url.
61 # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned.
62 #
63 # ==== Examples
64 #
65 # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git'
66 # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true
67 # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
68 #
69 def plugin(name, options)
70 log 'plugin', name
71
72 if options[:git] && options[:submodule]
73 in_root do
74 Git.run("submodule add #{options[:git]} vendor/plugins/#{name}")
75 end
76 elsif options[:git] || options[:svn]
77 in_root do
78 run_ruby_script("script/plugin install #{options[:svn] || options[:git]}", false)
79 end
80 else
81 log "! no git or svn provided for #{name}. skipping..."
82 end
83 end
84
85 # Adds an entry into config/environment.rb for the supplied gem :
86 def gem(name, options = {})
87 log 'gem', name
88 env = options.delete(:env)
89
90 gems_code = "config.gem '#{name}'"
91
92 if options.any?
93 opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ")
94 gems_code << ", #{opts}"
95 end
96
97 environment gems_code, :env => env
98 end
99
100 # Adds a line inside the Initializer block for config/environment.rb. Used by #gem
101 # If options :env is specified, the line is appended to the corresponding
102 # file in config/environments/#{env}.rb
103 def environment(data = nil, options = {}, &block)
104 sentinel = 'Rails::Initializer.run do |config|'
105
106 data = block.call if !data && block_given?
107
108 in_root do
109 if options[:env].nil?
110 gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
111 "#{match}\n " << data
112 end
113 else
114 Array.wrap(options[:env]).each do|env|
115 append_file "config/environments/#{env}.rb", "\n#{data}"
116 end
117 end
118 end
119 end
120
121 # Run a command in git.
122 #
123 # ==== Examples
124 #
125 # git :init
126 # git :add => "this.file that.rb"
127 # git :add => "onefile.rb", :rm => "badfile.cxx"
128 #
129 def git(command = {})
130 in_root do
131 if command.is_a?(Symbol)
132 log 'running', "git #{command}"
133 Git.run(command.to_s)
134 else
135 command.each do |command, options|
136 log 'running', "git #{command} #{options}"
137 Git.run("#{command} #{options}")
138 end
139 end
140 end
141 end
142
143 # Create a new file in the vendor/ directory. Code can be specified
144 # in a block or a data string can be given.
145 #
146 # ==== Examples
147 #
148 # vendor("sekrit.rb") do
149 # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--"
150 # "salt = '#{sekrit_salt}'"
151 # end
152 #
153 # vendor("foreign.rb", "# Foreign code is fun")
154 #
155 def vendor(filename, data = nil, &block)
156 log 'vendoring', filename
157 file("vendor/#{filename}", data, false, &block)
158 end
159
160 # Create a new file in the lib/ directory. Code can be specified
161 # in a block or a data string can be given.
162 #
163 # ==== Examples
164 #
165 # lib("crypto.rb") do
166 # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
167 # end
168 #
169 # lib("foreign.rb", "# Foreign code is fun")
170 #
171 def lib(filename, data = nil, &block)
172 log 'lib', filename
173 file("lib/#{filename}", data, false, &block)
174 end
175
176 # Create a new Rakefile with the provided code (either in a block or a string).
177 #
178 # ==== Examples
179 #
180 # rakefile("bootstrap.rake") do
181 # project = ask("What is the UNIX name of your project?")
182 #
183 # <<-TASK
184 # namespace :#{project} do
185 # task :bootstrap do
186 # puts "i like boots!"
187 # end
188 # end
189 # TASK
190 # end
191 #
192 # rakefile("seed.rake", "puts 'im plantin ur seedz'")
193 #
194 def rakefile(filename, data = nil, &block)
195 log 'rakefile', filename
196 file("lib/tasks/#{filename}", data, false, &block)
197 end
198
199 # Create a new initializer with the provided code (either in a block or a string).
200 #
201 # ==== Examples
202 #
203 # initializer("globals.rb") do
204 # data = ""
205 #
206 # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do
207 # data << "#{const} = :entp"
208 # end
209 #
210 # data
211 # end
212 #
213 # initializer("api.rb", "API_KEY = '123456'")
214 #
215 def initializer(filename, data = nil, &block)
216 log 'initializer', filename
217 file("config/initializers/#{filename}", data, false, &block)
218 end
219
220 # Generate something using a generator from Rails or a plugin.
221 # The second parameter is the argument string that is passed to
222 # the generator or an Array that is joined.
223 #
224 # ==== Example
225 #
226 # generate(:authenticated, "user session")
227 #
228 def generate(what, *args)
229 log 'generating', what
230 argument = args.map(&:to_s).flatten.join(" ")
231
232 in_root { run_ruby_script("script/generate #{what} #{argument}", false) }
233 end
234
235 # Executes a command
236 #
237 # ==== Example
238 #
239 # inside('vendor') do
240 # run('ln -s ~/edge rails)
241 # end
242 #
243 def run(command, log_action = true)
244 log 'executing', "#{command} from #{Dir.pwd}" if log_action
245 `#{command}`
246 end
247
248 # Executes a ruby script (taking into account WIN32 platform quirks)
249 def run_ruby_script(command, log_action = true)
250 ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : ''
251 run("#{ruby_command}#{command}", log_action)
252 end
253
254 # Runs the supplied rake task
255 #
256 # ==== Example
257 #
258 # rake("db:migrate")
259 # rake("db:migrate", :env => "production")
260 # rake("gems:install", :sudo => true)
261 #
262 def rake(command, options = {})
263 log 'rake', command
264 env = options[:env] || 'development'
265 sudo = options[:sudo] ? 'sudo ' : ''
266 in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) }
267 end
268
269 # Just run the capify command in root
270 #
271 # ==== Example
272 #
273 # capify!
274 #
275 def capify!
276 log 'capifying'
277 in_root { run('capify .', false) }
278 end
279
280 # Add Rails to /vendor/rails
281 #
282 # ==== Example
283 #
284 # freeze!
285 #
286 def freeze!(args = {})
287 log 'vendor', 'rails edge'
288 in_root { run('rake rails:freeze:edge', false) }
289 end
290
291 # Make an entry in Rails routing file conifg/routes.rb
292 #
293 # === Example
294 #
295 # route "map.root :controller => :welcome"
296 #
297 def route(routing_code)
298 log 'route', routing_code
299 sentinel = 'ActionController::Routing::Routes.draw do |map|'
300
301 in_root do
302 gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
303 "#{match}\n #{routing_code}\n"
304 end
305 end
306 end
307
308 protected
309
310 # Get a user's input
311 #
312 # ==== Example
313 #
314 # answer = ask("Should I freeze the latest Rails?")
315 # freeze! if ask("Should I freeze the latest Rails?") == "yes"
316 #
317 def ask(string)
318 log '', string
319 STDIN.gets.strip
320 end
321
322 # Do something in the root of the Rails application or
323 # a provided subfolder; the full path is yielded to the block you provide.
324 # The path is set back to the previous path when the method exits.
325 def inside(dir = '', &block)
326 folder = File.join(root, dir)
327 FileUtils.mkdir_p(folder) unless File.exist?(folder)
328 FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield }
329 end
330
331 def in_root
332 FileUtils.cd(root) { yield }
333 end
334
335 # Helper to test if the user says yes(y)?
336 #
337 # ==== Example
338 #
339 # freeze! if yes?("Should I freeze the latest Rails?")
340 #
341 def yes?(question)
342 answer = ask(question).downcase
343 answer == "y" || answer == "yes"
344 end
345
346 # Helper to test if the user does NOT say yes(y)?
347 #
348 # ==== Example
349 #
350 # capify! if no?("Will you be using vlad to deploy your application?")
351 #
352 def no?(question)
353 !yes?(question)
354 end
355
356 # Run a regular expression replacement on a file
357 #
358 # ==== Example
359 #
360 # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
361 #
362 def gsub_file(relative_destination, regexp, *args, &block)
363 path = destination_path(relative_destination)
364 content = File.read(path).gsub(regexp, *args, &block)
365 File.open(path, 'wb') { |file| file.write(content) }
366 end
367
368 # Append text to a file
369 #
370 # ==== Example
371 #
372 # append_file 'config/environments/test.rb', 'config.gem "rspec"'
373 #
374 def append_file(relative_destination, data)
375 path = destination_path(relative_destination)
376 File.open(path, 'ab') { |file| file.write(data) }
377 end
378
379 def destination_path(relative_destination)
380 File.join(root, relative_destination)
381 end
382
383 def log(action, message = '')
384 logger.log(action, message)
385 end
386
387 def logger
388 @logger ||= Rails::Generator::Base.logger
389 end
390
391 def logger
392 @logger ||= if defined?(Rails::Generator::Base)
393 Rails::Generator::Base.logger
394 else
395 require 'rails_generator/simple_logger'
396 Rails::Generator::SimpleLogger.new(STDOUT)
397 end
398 end
399
400 end
401 end