RubiGen::Commands::Create

Create is the premier generator command. It copies files, creates directories, renders templates, and more.

Constants

SYNONYM_LOOKUP_URI

Public Instance Methods

class_collisions(*class_names) click to toggle source

Check whether the given class names are already taken. In the future, expand to check other namespaces such as the rest of the user’s app.

     # File lib/rubigen/commands.rb, line 180
180:       def class_collisions(*class_names)
181:         class_names.flatten.each do |class_name|
182:           # Convert to string to allow symbol arguments.
183:           class_name = class_name.to_s
184: 
185:           # Skip empty strings.
186:           next if class_name.strip.empty?
187: 
188:           # Split the class from its module nesting.
189:           nesting = class_name.split('::')
190:           name = nesting.pop
191: 
192:           # Extract the last Module in the nesting.
193:           last = nesting.inject(Object) { |last, nest|
194:             break unless last.const_defined?(nest)
195:             last.const_get(nest)
196:           }
197: 
198:           # If the last Module exists, check whether the given
199:           # class exists and raise a collision if so.
200:           if last and last.const_defined?(name.camelize)
201:             raise_class_collision(class_name)
202:           end
203:         end
204:       end
complex_template(relative_source, relative_destination, template_options = {}) click to toggle source
     # File lib/rubigen/commands.rb, line 335
335:       def complex_template(relative_source, relative_destination, template_options = {})
336:         options = template_options.dup
337:         options[:assigns] ||= {}
338:         options[:assigns]['template_for_inclusion'] = render_template_part(template_options)
339:         template(relative_source, relative_destination, options)
340:       end
directory(relative_path) click to toggle source

Create a directory including any missing parent directories. Always skips directories which exist.

     # File lib/rubigen/commands.rb, line 344
344:       def directory(relative_path)
345:         path = destination_path(relative_path)
346:         if File.exist?(path)
347:           logger.exists relative_path
348:         else
349:           logger.create relative_path
350:           unless options[:pretend]
351:             FileUtils.mkdir_p(path)
352:             # git doesn't require adding the paths, adding the files later will
353:             # automatically do a path add.
354: 
355:             # Subversion doesn't do path adds, so we need to add
356:             # each directory individually.
357:             # So stack up the directory tree and add the paths to
358:             # subversion in order without recursion.
359:             if options[:svn]
360:               stack = [relative_path]
361:               until File.dirname(stack.last) == stack.last # dirname('.') == '.'
362:                 stack.push File.dirname(stack.last)
363:               end
364:               stack.reverse_each do |rel_path|
365:                 svn_path = destination_path(rel_path)
366:                 system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
367:               end
368:             end
369:           end
370:         end
371:       end
file(relative_source, relative_destination, file_options = {}, &block) click to toggle source

Copy a file from source to destination with collision checking.

The file_options hash accepts :chmod and :shebang and :collision options. :chmod sets the permissions of the destination file:

  file 'config/empty.log', 'log/test.log', :chmod => 0664

:shebang sets the #!/usr/bin/ruby line for scripts

  file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby'

:collision sets the collision option only for the destination file:

  file 'settings/server.yml', 'config/server.yml', :collision => :skip

Collisions are handled by checking whether the destination file exists and either skipping the file, forcing overwrite, or asking the user what to do.

     # File lib/rubigen/commands.rb, line 219
219:       def file(relative_source, relative_destination, file_options = {}, &block)
220:         # Determine full paths for source and destination files.
221:         source              = source_path(relative_source)
222:         destination         = destination_path(relative_destination)
223:         destination_exists  = File.exist?(destination)
224: 
225:         # If source and destination are identical then we're done.
226:         if destination_exists and identical?(source, destination, &block)
227:           return logger.identical(relative_destination)
228:         end
229: 
230:         # Check for and resolve file collisions.
231:         if destination_exists
232: 
233:           # Make a choice whether to overwrite the file.  :force and
234:           # :skip already have their mind made up, but give :ask a shot.
235:           choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask
236:             when :ask   then force_file_collision?(relative_destination, source, destination, file_options, &block)
237:             when :force then :force
238:             when :skip  then :skip
239:             else raise "Invalid collision option: #{options[:collision].inspect}"
240:           end
241: 
242:           # Take action based on our choice.  Bail out if we chose to
243:           # skip the file; otherwise, log our transgression and continue.
244:           case choice
245:             when :force then logger.force(relative_destination)
246:             when :skip  then return(logger.skip(relative_destination))
247:             else raise "Invalid collision choice: #{choice}.inspect"
248:           end
249: 
250:         # File doesn't exist so log its unbesmirched creation.
251:         else
252:           logger.create relative_destination
253:         end
254: 
255:         # If we're pretending, back off now.
256:         return if options[:pretend]
257: 
258:         # Write destination file with optional shebang.  Yield for content
259:         # if block given so templaters may render the source file.  If a
260:         # shebang is requested, replace the existing shebang or insert a
261:         # new one.
262:         File.open(destination, 'wb') do |dest|
263:           dest.write render_file(source, file_options, &block)
264:         end
265: 
266:         # Optionally change permissions.
267:         if file_options[:chmod]
268:           FileUtils.chmod(file_options[:chmod], destination)
269:         end
270: 
271:         # Optionally add file to subversion or git
272:         system("svn add #{destination}") if options[:svn]
273:       end
file_copy_each(files, path=nil, options = {}) click to toggle source
     # File lib/rubigen/commands.rb, line 275
275:       def file_copy_each(files, path=nil, options = {})
276:         path = path ? "#{path}/" : ""
277:         files.each do |file_name|
278:           file "#{path}#{file_name}", "#{path}#{file_name}", options
279:         end
280:       end
folder(template_path, path=nil, options = {}) click to toggle source
     # File lib/rubigen/commands.rb, line 282
282:       def folder(template_path, path=nil, options = {})
283:         template_path = "/" if template_path.blank?
284:         source = source_path(template_path)
285:         files  = Dir[source + '/*'].select { |file| File.file? file }.map { |file| file.sub(/^#{source}/,"") }
286:         files.each do |file_name|
287:           file "#{template_path}#{file_name}", "#{path}#{file_name}", options
288:         end
289:         system("git add -v #{relative_destination}") if options[:git]
290:       end
identical?(source, destination, &block) click to toggle source

Checks if the source and the destination file are identical. If passed a block then the source file is a template that needs to first be evaluated before being compared to the destination.

     # File lib/rubigen/commands.rb, line 295
295:       def identical?(source, destination, &block)
296:         return false if File.directory? destination
297:         source      = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source)
298:         destination = IO.read(destination)
299:         source == destination
300:       end
migration_template(relative_source, relative_destination, template_options = {}) click to toggle source

When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.

     # File lib/rubigen/commands.rb, line 419
419:       def migration_template(relative_source, relative_destination, template_options = {})
420:         migration_directory relative_destination
421:         migration_file_name = template_options[:migration_file_name] || file_name
422:         raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name)
423:         template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options)
424:       end
readme(*relative_sources) click to toggle source

Display a README.

     # File lib/rubigen/commands.rb, line 374
374:       def readme(*relative_sources)
375:         relative_sources.flatten.each do |relative_source|
376:           logger.readme relative_source
377:           stdout.puts File.read(source_path(relative_source)) unless options[:pretend]
378:         end
379:       end
route_resources(*resources) click to toggle source
     # File lib/rubigen/commands.rb, line 426
426:       def route_resources(*resources)
427:         resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
428:         sentinel = 'ActionController::Routing::Routes.draw do |map|'
429: 
430:         logger.route "map.resources #{resource_list}"
431:         unless options[:pretend]
432:           gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/i do |match|
433:             "#{match}\n  map.resources #{resource_list}\n"
434:           end
435:         end
436:       end
template(relative_source, relative_destination, template_options = {}) click to toggle source

Generate a file using an ERuby template. Looks up and evaluates a template by name and writes the result.

The ERB template uses explicit trim mode to best control the proliferation of whitespace in generated code. <%- trims leading whitespace; -%> trims trailing whitespace including one newline.

A hash of template options may be passed as the last argument. The options accepted by the file are accepted as well as :assigns, a hash of variable bindings. Example:

  template 'foo', 'bar', :assigns => { :action => 'view' }

Template is implemented in terms of file. It calls file with a block which takes a file handle and returns its rendered contents.

     # File lib/rubigen/commands.rb, line 316
316:       def template(relative_source, relative_destination, template_options = {})
317:         file(relative_source, relative_destination, template_options) do |file|
318:           # Evaluate any assignments in a temporary, throwaway binding.
319:           vars = template_options[:assigns] || {}
320:           b = binding
321:           vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
322: 
323:           # Render the source file with the temporary binding.
324:           ERB.new(file.read, nil, '-').result(b)
325:         end
326:       end
template_copy_each(files, path = nil, options = {}) click to toggle source
     # File lib/rubigen/commands.rb, line 328
328:       def template_copy_each(files, path = nil, options = {})
329:         path = path ? "#{path}/" : ""
330:         files.each do |file_name|
331:           template "#{path}#{file_name}", "#{path}#{file_name.gsub(/\.erb$/,'')}", options
332:         end
333:       end
write_manifest(relative_destination) click to toggle source
     # File lib/rubigen/commands.rb, line 381
381:       def write_manifest(relative_destination)
382:         files = ([relative_destination] + Dir["#{destination_root}/**/*"])
383:         files.reject! { |file| File.directory?(file) }
384:         files.map! { |path| path.sub("#{destination_root}/","") }
385:         files = files.uniq.sort
386: 
387: 
388:         destination         = destination_path(relative_destination)
389:         destination_exists  = File.exists?(destination)
390: 
391:         # Check for and resolve file collisions.
392:         if destination_exists
393:           # Always recreate the Manifest (perhaps we need to give the option... like normal files)
394:           choice = :force
395:           logger.force(relative_destination)
396: 
397:         # File doesn't exist so log its unbesmirched creation.
398:         else
399:           logger.create relative_destination
400:         end
401: 
402:         # If we're pretending, back off now.
403:         return if options[:pretend]
404: 
405:         # Write destination file with optional shebang.  Yield for content
406:         # if block given so templaters may render the source file.  If a
407:         # shebang is requested, replace the existing shebang or insert a
408:         # new one.
409:         File.open(destination, 'wb') do |dest|
410:           dest.write files.join("\n")
411:           dest.write "\n"
412:         end
413: 
414:         # Optionally add file to subversion
415:         system("svn add #{destination}") if options[:svn]
416:       end

Private Instance Methods

find_synonyms(word) click to toggle source

Look up synonyms on WordNet. Thanks to Florian Gross (flgr).

     # File lib/rubigen/commands.rb, line 475
475:         def find_synonyms(word)
476:           require 'open-uri'
477:           require 'timeout'
478:           timeout(5) do
479:             open(SYNONYM_LOOKUP_URI % word) do |stream|
480:               # Grab words linked to dictionary entries as possible synonyms
481:               data = stream.read.gsub("&nbsp;", " ").scan(/<a href="webwn.*?">([\w ]*?)<\/a>/).uniq
482:             end
483:           end
484:         rescue Exception
485:           return nil
486:         end
raise_class_collision(class_name) click to toggle source

Raise a usage error with an informative WordNet suggestion. Thanks to Florian Gross (flgr).

     # File lib/rubigen/commands.rb, line 458
458:         def raise_class_collision(class_name)
459:           message = The name '#{class_name}' is either already used in your application or reserved.Please choose an alternative and run this generator again.
460:           if suggest = find_synonyms(class_name)
461:             if suggest.any?
462:               message << "\n  Suggestions:  \n\n"
463:               message << suggest.join("\n")
464:             end
465:           end
466:           raise UsageError, message
467:         end
render_file(path, options = {}) click to toggle source
     # File lib/rubigen/commands.rb, line 439
439:         def render_file(path, options = {})
440:           File.open(path, 'rb') do |file|
441:             if block_given?
442:               yield file
443:             else
444:               content = ''
445:               if shebang = options[:shebang]
446:                 content << "#!#{shebang}\n"
447:                 if line = file.gets
448:                   content << "line\n" if line !~ /^#!/
449:                 end
450:               end
451:               content << file.read
452:             end
453:           end
454:         end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.