Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 618 lines (536 sloc) 24.704 kB
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
1 require 'delegate'
2 require 'optparse'
3 require 'fileutils'
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
4 require 'tempfile'
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
5 require 'erb'
6
7 module Rails
8 module Generator
9 module Commands
10 # Here's a convenient way to get a handle on generator commands.
11 # Command.instance('destroy', my_generator) instantiates a Destroy
12 # delegate of my_generator ready to do your dirty work.
13 def self.instance(command, generator)
14 const_get(command.to_s.camelize).new(generator)
15 end
16
17 # Even more convenient access to commands. Include Commands in
18 # the generator Base class to get a nice #command instance method
19 # which returns a delegate for the requested command.
6186490 Replace Ruby's deprecated append_features in favor of included. [Marc…
Marcel Molina authored
20 def self.included(base)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
21 base.send(:define_method, :command) do |command|
22 Commands.instance(command, self)
23 end
24 end
25
26
27 # Generator commands delegate Rails::Generator::Base and implement
28 # a standard set of actions. Their behavior is defined by the way
29 # they respond to these actions: Create brings life; Destroy brings
30 # death; List passively observes.
31 #
32 # Commands are invoked by replaying (or rewinding) the generator's
33 # manifest of actions. See Rails::Generator::Manifest and
34 # Rails::Generator::Base#manifest method that generator subclasses
35 # are required to override.
36 #
37 # Commands allows generators to "plug in" invocation behavior, which
38 # corresponds to the GoF Strategy pattern.
39 class Base < DelegateClass(Rails::Generator::Base)
40 # Replay action manifest. RewindBase subclass rewinds manifest.
41 def invoke!
42 manifest.replay(self)
e8cc4b1 @jm Add "-m/--template" option to Rails generator to apply template to ge…
jm authored
43 after_generate
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
44 end
45
46 def dependency(generator_name, args, runtime_options = {})
47 logger.dependency(generator_name) do
48 self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke!
49 end
50 end
51
52 # Does nothing for all commands except Create.
53 def class_collisions(*class_names)
54 end
55
56 # Does nothing for all commands except Create.
57 def readme(*args)
58 end
59
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
60 protected
bbab639 @nikz Set config.active_record.timestamped_migrations = false to have migra…
nikz authored
61 def current_migration_number
62 Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path|
63 n = File.basename(file_path).split('_', 2).first.to_i
64 if n > max then n else max end
65 end
66 end
67
68 def next_migration_number
69 current_migration_number + 1
70 end
71
c2bb269 @jeremy Allows generator to specify migrations directory. References #2960.
jeremy authored
72 def migration_directory(relative_path)
73 directory(@migration_directory = relative_path)
74 end
75
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
76 def existing_migrations(file_name)
30c6bd9 Make migration generator only report on exact duplicate names, not pa…
Marcel Molina authored
77 Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/)
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
78 end
79
80 def migration_exists?(file_name)
81 not existing_migrations(file_name).empty?
82 end
83
84 def next_migration_string(padding = 3)
bbab639 @nikz Set config.active_record.timestamped_migrations = false to have migra…
nikz authored
85 if ActiveRecord::Base.timestamped_migrations
86 Time.now.utc.strftime("%Y%m%d%H%M%S")
87 else
88 "%.#{padding}d" % next_migration_number
89 end
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
90 end
91
c5dcec7 @jeremy resource and scaffold_resource generators add a restful route to conf…
jeremy authored
92 def gsub_file(relative_destination, regexp, *args, &block)
93 path = destination_path(relative_destination)
94 content = File.read(path).gsub(regexp, *args, &block)
95 File.open(path, 'wb') { |file| file.write(content) }
96 end
97
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
98 private
99 # Ask the user interactively whether to force collision.
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
100 def force_file_collision?(destination, src, dst, file_options = {}, &block)
c8d9a07 @dhh Added better documentation for generator overwrite options (closes #9…
dhh authored
101 $stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] "
102 case $stdin.gets.chomp
103 when /\Ad\z/i
47f9958 @jeremy Generator: use destination path for diff tempfiles. Closes #7015.
jeremy authored
104 Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp|
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
105 temp.write render_file(src, file_options, &block)
106 temp.rewind
a81f16a @badcarl Ensure Rails::Generator quotes file names while generating diff. [#26…
badcarl authored
107 $stdout.puts `#{diff_cmd} "#{dst}" "#{temp.path}"`
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
108 end
109 puts "retrying"
110 raise 'retry diff'
c8d9a07 @dhh Added better documentation for generator overwrite options (closes #9…
dhh authored
111 when /\Aa\z/i
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
112 $stdout.puts "forcing #{spec.name}"
113 options[:collision] = :force
c8d9a07 @dhh Added better documentation for generator overwrite options (closes #9…
dhh authored
114 when /\Aq\z/i
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
115 $stdout.puts "aborting #{spec.name}"
116 raise SystemExit
c8d9a07 @dhh Added better documentation for generator overwrite options (closes #9…
dhh authored
117 when /\An\z/i then :skip
118 when /\Ay\z/i then :force
119 else
120 $stdout.puts <<-HELP
121 Y - yes, overwrite
122 n - no, do not overwrite
123 a - all, overwrite this and all others
124 q - quit, abort
125 d - diff, show the differences between the old and the new
126 h - help, show this help
127 HELP
128 raise 'retry'
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
129 end
130 rescue
131 retry
132 end
133
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
134 def diff_cmd
135 ENV['RAILS_DIFF'] || 'diff -u'
136 end
137
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
138 def render_template_part(template_options)
139 # Getting Sandbox to evaluate part template in it
140 part_binding = template_options[:sandbox].call.sandbox_binding
141 part_rel_path = template_options[:insert]
142 part_path = source_path(part_rel_path)
143
144 # Render inner template within Sandbox binding
145 rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding)
146 begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id])
147 end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id])
148 begin_mark + rendered_part + end_mark
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
149 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
150
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
151 def template_part_mark(name, id)
152 "<!--[#{name}:#{id}]-->\n"
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
153 end
154 end
155
156 # Base class for commands which handle generator actions in reverse, such as Destroy.
157 class RewindBase < Base
158 # Rewind action manifest.
159 def invoke!
160 manifest.rewind(self)
161 end
162 end
163
164
165 # Create is the premier generator command. It copies files, creates
166 # directories, renders templates, and more.
167 class Create < Base
168
169 # Check whether the given class names are already taken by
170 # Ruby or Rails. In the future, expand to check other namespaces
171 # such as the rest of the user's app.
172 def class_collisions(*class_names)
9ab83b1 @josh Don't include the path when checking class collisions [#545 state:res…
josh authored
173 path = class_names.shift
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
174 class_names.flatten.each do |class_name|
175 # Convert to string to allow symbol arguments.
176 class_name = class_name.to_s
177
178 # Skip empty strings.
f522a89 @josh Revert "Fixed generator collisions for nested controller modules."
josh authored
179 next if class_name.strip.empty?
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
180
181 # Split the class from its module nesting.
182 nesting = class_name.split('::')
183 name = nesting.pop
184
185 # Extract the last Module in the nesting.
f522a89 @josh Revert "Fixed generator collisions for nested controller modules."
josh authored
186 last = nesting.inject(Object) { |last, nest|
187 break unless last.const_defined?(nest)
188 last.const_get(nest)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
189 }
190
f522a89 @josh Revert "Fixed generator collisions for nested controller modules."
josh authored
191 # If the last Module exists, check whether the given
192 # class exists and raise a collision if so.
193 if last and last.const_defined?(name.camelize)
194 raise_class_collision(class_name)
195 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
196 end
197 end
198
199 # Copy a file from source to destination with collision checking.
200 #
647130d @dhh Added collision option to template generation in generators (closes #…
dhh authored
201 # The file_options hash accepts :chmod and :shebang and :collision options.
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
202 # :chmod sets the permissions of the destination file:
203 # file 'config/empty.log', 'log/test.log', :chmod => 0664
204 # :shebang sets the #!/usr/bin/ruby line for scripts
205 # file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby'
2681d55 @jeremy Update generator tests. Closes #11487 [thechrisoshow]
jeremy authored
206 # :collision sets the collision option only for the destination file:
647130d @dhh Added collision option to template generation in generators (closes #…
dhh authored
207 # file 'settings/server.yml', 'config/server.yml', :collision => :skip
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
208 #
209 # Collisions are handled by checking whether the destination file
210 # exists and either skipping the file, forcing overwrite, or asking
211 # the user what to do.
a7cdaad Evaluate dynamic templates before checking if the new file is identic…
Marcel Molina authored
212 def file(relative_source, relative_destination, file_options = {}, &block)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
213 # Determine full paths for source and destination files.
d451044 Make the generator skip a file if it already exists and is identical …
Marcel Molina authored
214 source = source_path(relative_source)
215 destination = destination_path(relative_destination)
b1ce7e4 @jeremy Ruby 1.9 compat: File.exists\? -> File.exist\? en masse. References #…
jeremy authored
216 destination_exists = File.exist?(destination)
a7cdaad Evaluate dynamic templates before checking if the new file is identic…
Marcel Molina authored
217
218 # If source and destination are identical then we're done.
219 if destination_exists and identical?(source, destination, &block)
2681d55 @jeremy Update generator tests. Closes #11487 [thechrisoshow]
jeremy authored
220 return logger.identical(relative_destination)
a7cdaad Evaluate dynamic templates before checking if the new file is identic…
Marcel Molina authored
221 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
222
223 # Check for and resolve file collisions.
d451044 Make the generator skip a file if it already exists and is identical …
Marcel Molina authored
224 if destination_exists
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
225
226 # Make a choice whether to overwrite the file. :force and
227 # :skip already have their mind made up, but give :ask a shot.
647130d @dhh Added collision option to template generation in generators (closes #…
dhh authored
228 choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
229 when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
230 when :force then :force
231 when :skip then :skip
232 else raise "Invalid collision option: #{options[:collision].inspect}"
233 end
234
235 # Take action based on our choice. Bail out if we chose to
236 # skip the file; otherwise, log our transgression and continue.
237 case choice
238 when :force then logger.force(relative_destination)
239 when :skip then return(logger.skip(relative_destination))
240 else raise "Invalid collision choice: #{choice}.inspect"
241 end
242
243 # File doesn't exist so log its unbesmirched creation.
244 else
245 logger.create relative_destination
246 end
247
248 # If we're pretending, back off now.
249 return if options[:pretend]
250
251 # Write destination file with optional shebang. Yield for content
252 # if block given so templaters may render the source file. If a
253 # shebang is requested, replace the existing shebang or insert a
254 # new one.
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
255 File.open(destination, 'wb') do |dest|
256 dest.write render_file(source, file_options, &block)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
257 end
258
259 # Optionally change permissions.
260 if file_options[:chmod]
261 FileUtils.chmod(file_options[:chmod], destination)
262 end
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
263
7527447 @jeremy Git support for script/generate. Closes #10690.
jeremy authored
264 # Optionally add file to subversion or git
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
265 system("svn add #{destination}") if options[:svn]
7527447 @jeremy Git support for script/generate. Closes #10690.
jeremy authored
266 system("git add -v #{relative_destination}") if options[:git]
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
267 end
268
a7cdaad Evaluate dynamic templates before checking if the new file is identic…
Marcel Molina authored
269 # Checks if the source and the destination file are identical. If
270 # passed a block then the source file is a template that needs to first
271 # be evaluated before being compared to the destination.
272 def identical?(source, destination, &block)
6b59011 Skip directories when checking for identical source and destination f…
Marcel Molina authored
273 return false if File.directory? destination
a7cdaad Evaluate dynamic templates before checking if the new file is identic…
Marcel Molina authored
274 source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source)
275 destination = IO.read(destination)
24b9d2f Backing out of #2496. Comparing md5 checksums is in no way faster tha…
Marcel Molina authored
276 source == destination
d451044 Make the generator skip a file if it already exists and is identical …
Marcel Molina authored
277 end
278
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
279 # Generate a file for a Rails application using an ERuby template.
e3b49c0 @dhh Fixed spelling errors (closes #9706) [tarmo/rmm5t]
dhh authored
280 # Looks up and evaluates a template by name and writes the result.
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
281 #
282 # The ERB template uses explicit trim mode to best control the
283 # proliferation of whitespace in generated code. <%- trims leading
284 # whitespace; -%> trims trailing whitespace including one newline.
285 #
286 # A hash of template options may be passed as the last argument.
287 # The options accepted by the file are accepted as well as :assigns,
288 # a hash of variable bindings. Example:
289 # template 'foo', 'bar', :assigns => { :action => 'view' }
290 #
291 # Template is implemented in terms of file. It calls file with a
292 # block which takes a file handle and returns its rendered contents.
293 def template(relative_source, relative_destination, template_options = {})
294 file(relative_source, relative_destination, template_options) do |file|
295 # Evaluate any assignments in a temporary, throwaway binding.
296 vars = template_options[:assigns] || {}
297 b = binding
298 vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
299
300 # Render the source file with the temporary binding.
301 ERB.new(file.read, nil, '-').result(b)
302 end
303 end
304
305 def complex_template(relative_source, relative_destination, template_options = {})
306 options = template_options.dup
307 options[:assigns] ||= {}
308 options[:assigns]['template_for_inclusion'] = render_template_part(template_options)
309 template(relative_source, relative_destination, options)
310 end
311
312 # Create a directory including any missing parent directories.
7527447 @jeremy Git support for script/generate. Closes #10690.
jeremy authored
313 # Always skips directories which exist.
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
314 def directory(relative_path)
315 path = destination_path(relative_path)
b1ce7e4 @jeremy Ruby 1.9 compat: File.exists\? -> File.exist\? en masse. References #…
jeremy authored
316 if File.exist?(path)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
317 logger.exists relative_path
318 else
319 logger.create relative_path
2681d55 @jeremy Update generator tests. Closes #11487 [thechrisoshow]
jeremy authored
320 unless options[:pretend]
321 FileUtils.mkdir_p(path)
322 # git doesn't require adding the paths, adding the files later will
323 # automatically do a path add.
324
325 # Subversion doesn't do path adds, so we need to add
326 # each directory individually.
327 # So stack up the directory tree and add the paths to
328 # subversion in order without recursion.
329 if options[:svn]
330 stack = [relative_path]
331 until File.dirname(stack.last) == stack.last # dirname('.') == '.'
332 stack.push File.dirname(stack.last)
333 end
334 stack.reverse_each do |rel_path|
335 svn_path = destination_path(rel_path)
336 system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
337 end
338 end
14f6440 @technoweenie Correct indentation in a couple spots. Closes #10671 [l.guidi, rick]
technoweenie authored
339 end
340 end
341 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
342
343 # Display a README.
344 def readme(*relative_sources)
345 relative_sources.flatten.each do |relative_source|
346 logger.readme relative_source
347 puts File.read(source_path(relative_source)) unless options[:pretend]
348 end
349 end
350
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
351 # When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
352 def migration_template(relative_source, relative_destination, template_options = {})
c2bb269 @jeremy Allows generator to specify migrations directory. References #2960.
jeremy authored
353 migration_directory relative_destination
1aea470 @dhh Added tmp/sessions, tmp/cache, and tmp/sockets as default directories…
dhh authored
354 migration_file_name = template_options[:migration_file_name] || file_name
355 raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name)
356 template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options)
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
357 end
358
c5dcec7 @jeremy resource and scaffold_resource generators add a restful route to conf…
jeremy authored
359 def route_resources(*resources)
360 resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
361 sentinel = 'ActionController::Routing::Routes.draw do |map|'
362
363 logger.route "map.resources #{resource_list}"
29ec023 @technoweenie Fix scaffold_resource generator so it respects the --pretend argument…
technoweenie authored
364 unless options[:pretend]
365 gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
366 "#{match}\n map.resources #{resource_list}\n"
367 end
c5dcec7 @jeremy resource and scaffold_resource generators add a restful route to conf…
jeremy authored
368 end
369 end
370
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
371 private
868d9f5 @jeremy Generator can show diff on file collision to help you decide whether …
jeremy authored
372 def render_file(path, options = {})
373 File.open(path, 'rb') do |file|
374 if block_given?
375 yield file
376 else
377 content = ''
378 if shebang = options[:shebang]
379 content << "#!#{shebang}\n"
380 if line = file.gets
381 content << "line\n" if line !~ /^#!/
382 end
383 end
384 content << file.read
385 end
386 end
387 end
388
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
389 # Raise a usage error with an informative WordNet suggestion.
390 # Thanks to Florian Gross (flgr).
391 def raise_class_collision(class_name)
392 message = <<end_message
9b75483 @dhh Added better error message for when the class name is already used (a…
dhh authored
393 The name '#{class_name}' is either already used in your application or reserved by Ruby on Rails.
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
394 Please choose an alternative and run this generator again.
395 end_message
396 if suggest = find_synonyms(class_name)
9b75483 @dhh Added better error message for when the class name is already used (a…
dhh authored
397 if suggest.any?
398 message << "\n Suggestions: \n\n"
399 message << suggest.join("\n")
400 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
401 end
402 raise UsageError, message
403 end
404
3642a4d @jeremy Resurrect WordNet synonym lookups. Closes #10710.
jeremy authored
405 SYNONYM_LOOKUP_URI = "http://wordnet.princeton.edu/perl/webwn?s=%s"
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
406
407 # Look up synonyms on WordNet. Thanks to Florian Gross (flgr).
408 def find_synonyms(word)
409 require 'open-uri'
410 require 'timeout'
411 timeout(5) do
412 open(SYNONYM_LOOKUP_URI % word) do |stream|
3642a4d @jeremy Resurrect WordNet synonym lookups. Closes #10710.
jeremy authored
413 # Grab words linked to dictionary entries as possible synonyms
414 data = stream.read.gsub("&nbsp;", " ").scan(/<a href="webwn.*?">([\w ]*?)<\/a>/s).uniq
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
415 end
416 end
417 rescue Exception
418 return nil
419 end
420 end
421
422
423 # Undo the actions performed by a generator. Rewind the action
424 # manifest and attempt to completely erase the results of each action.
425 class Destroy < RewindBase
426 # Remove a file if it exists and is a file.
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
427 def file(relative_source, relative_destination, file_options = {})
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
428 destination = destination_path(relative_destination)
b1ce7e4 @jeremy Ruby 1.9 compat: File.exists\? -> File.exist\? en masse. References #…
jeremy authored
429 if File.exist?(destination)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
430 logger.rm relative_destination
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
431 unless options[:pretend]
432 if options[:svn]
433 # If the file has been marked to be added
72ef2be @dhh Fixed missing ds
dhh authored
434 # but has not yet been checked in, revert and delete
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
435 if options[:svn][relative_destination]
436 system("svn revert #{destination}")
437 FileUtils.rm(destination)
438 else
439 # If the directory is not in the status list, it
440 # has no modifications so we can simply remove it
441 system("svn rm #{destination}")
7527447 @jeremy Git support for script/generate. Closes #10690.
jeremy authored
442 end
443 elsif options[:git]
444 if options[:git][:new][relative_destination]
445 # file has been added, but not committed
446 system("git reset HEAD #{relative_destination}")
447 FileUtils.rm(destination)
448 elsif options[:git][:modified][relative_destination]
449 # file is committed and modified
450 system("git rm -f #{relative_destination}")
451 else
452 # If the directory is not in the status list, it
453 # has no modifications so we can simply remove it
454 system("git rm #{relative_destination}")
455 end
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
456 else
457 FileUtils.rm(destination)
458 end
459 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
460 else
461 logger.missing relative_destination
462 return
463 end
464 end
465
466 # Templates are deleted just like files and the actions take the
467 # same parameters, so simply alias the file method.
468 alias_method :template, :file
469
470 # Remove each directory in the given path from right to left.
471 # Remove each subdirectory if it exists and is a directory.
472 def directory(relative_path)
473 parts = relative_path.split('/')
474 until parts.empty?
475 partial = File.join(parts)
476 path = destination_path(partial)
b1ce7e4 @jeremy Ruby 1.9 compat: File.exists\? -> File.exist\? en masse. References #…
jeremy authored
477 if File.exist?(path)
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
478 if Dir[File.join(path, '*')].empty?
479 logger.rmdir partial
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
480 unless options[:pretend]
481 if options[:svn]
482 # If the directory has been marked to be added
72ef2be @dhh Fixed missing ds
dhh authored
483 # but has not yet been checked in, revert and delete
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
484 if options[:svn][relative_path]
485 system("svn revert #{path}")
486 FileUtils.rmdir(path)
487 else
488 # If the directory is not in the status list, it
489 # has no modifications so we can simply remove it
490 system("svn rm #{path}")
491 end
7527447 @jeremy Git support for script/generate. Closes #10690.
jeremy authored
492 # I don't think git needs to remove directories?..
493 # or maybe they have special consideration...
b33557b @dhh Added -c/--svn option to the generator that'll add new files and remo…
dhh authored
494 else
495 FileUtils.rmdir(path)
496 end
497 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
498 else
499 logger.notempty partial
500 end
501 else
502 logger.missing partial
503 end
504 parts.pop
505 end
506 end
507
508 def complex_template(*args)
509 # nothing should be done here
510 end
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
511
512 # When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}".
513 def migration_template(relative_source, relative_destination, template_options = {})
c2bb269 @jeremy Allows generator to specify migrations directory. References #2960.
jeremy authored
514 migration_directory relative_destination
5f4f4b0 @dhh Fix for destroying migrations made when generating a model (closes #4…
dhh authored
515
516 migration_file_name = template_options[:migration_file_name] || file_name
8935209 Don't raise when the migration file is missing or it'll stop destroy …
Scott Barron authored
517 unless migration_exists?(migration_file_name)
518 puts "There is no migration named #{migration_file_name}"
519 return
520 end
521
5f4f4b0 @dhh Fix for destroying migrations made when generating a model (closes #4…
dhh authored
522
523 existing_migrations(migration_file_name).each do |file_path|
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
524 file(relative_source, file_path, template_options)
525 end
526 end
c5dcec7 @jeremy resource and scaffold_resource generators add a restful route to conf…
jeremy authored
527
528 def route_resources(*resources)
529 resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
530 look_for = "\n map.resources #{resource_list}\n"
531 logger.route "map.resources #{resource_list}"
532 gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
533 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
534 end
535
536
537 # List a generator's action manifest.
538 class List < Base
539 def dependency(generator_name, args, options = {})
540 logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})"
541 end
542
543 def class_collisions(*class_names)
544 logger.class_collisions class_names.join(', ')
545 end
546
547 def file(relative_source, relative_destination, options = {})
548 logger.file relative_destination
549 end
550
551 def template(relative_source, relative_destination, options = {})
552 logger.template relative_destination
553 end
554
555 def complex_template(relative_source, relative_destination, options = {})
556 logger.template "#{options[:insert]} inside #{relative_destination}"
557 end
558
559 def directory(relative_path)
560 logger.directory "#{destination_path(relative_path)}/"
561 end
562
563 def readme(*args)
564 logger.readme args.join(', ')
565 end
2681d55 @jeremy Update generator tests. Closes #11487 [thechrisoshow]
jeremy authored
566
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
567 def migration_template(relative_source, relative_destination, options = {})
c2bb269 @jeremy Allows generator to specify migrations directory. References #2960.
jeremy authored
568 migration_directory relative_destination
9b9578f @jeremy Introducing the session_migration generator. Creates an add_session_…
jeremy authored
569 logger.migration_template file_name
570 end
c5dcec7 @jeremy resource and scaffold_resource generators add a restful route to conf…
jeremy authored
571
572 def route_resources(*resources)
573 resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
574 logger.route "map.resources #{resource_list}"
575 end
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
576 end
577
578 # Update generator's action manifest.
579 class Update < Create
580 def file(relative_source, relative_destination, options = {})
581 # logger.file relative_destination
582 end
583
584 def template(relative_source, relative_destination, options = {})
585 # logger.template relative_destination
586 end
587
588 def complex_template(relative_source, relative_destination, template_options = {})
589
590 begin
591 dest_file = destination_path(relative_destination)
592 source_to_update = File.readlines(dest_file).join
593 rescue Errno::ENOENT
594 logger.missing relative_destination
595 return
596 end
597
21187c0 @dhh Apply the rest of Chads patch
dhh authored
598 logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}"
daee6fd @dhh Added new generator framework that informs about its doings on genera…
dhh authored
599
600 begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id]))
601 end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id]))
602
603 # Refreshing inner part of the template with freshly rendered part.
604 rendered_part = render_template_part(template_options)
605 source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part)
606
607 File.open(dest_file, 'w') { |file| file.write(source_to_update) }
608 end
609
610 def directory(relative_path)
611 # logger.directory "#{destination_path(relative_path)}/"
612 end
613 end
614
615 end
616 end
617 end
Something went wrong with that request. Please try again.