diff options
author | José Valim <jose.valim@gmail.com> | 2009-07-03 12:32:46 +0200 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2009-07-03 14:57:46 +0200 |
commit | 3ca504c966b8cf3ad51cdccd3377efc33fccab33 (patch) | |
tree | 7a0c363e18480c3e29ab72e1ffb53e9d6734908a /railties/lib/rails_generator/commands.rb | |
parent | de68cf7e0624e7d8e7e94034858a061ca0f6f68c (diff) | |
download | rails-3ca504c966b8cf3ad51cdccd3377efc33fccab33.tar.gz rails-3ca504c966b8cf3ad51cdccd3377efc33fccab33.tar.bz2 rails-3ca504c966b8cf3ad51cdccd3377efc33fccab33.zip |
Removed remaining old generators files.
Diffstat (limited to 'railties/lib/rails_generator/commands.rb')
-rw-r--r-- | railties/lib/rails_generator/commands.rb | 621 |
1 files changed, 0 insertions, 621 deletions
diff --git a/railties/lib/rails_generator/commands.rb b/railties/lib/rails_generator/commands.rb deleted file mode 100644 index b684dc92be..0000000000 --- a/railties/lib/rails_generator/commands.rb +++ /dev/null @@ -1,621 +0,0 @@ -require 'delegate' -require 'optparse' -require 'fileutils' -require 'tempfile' -require 'erb' - -module Rails - module Generator - module Commands - # Here's a convenient way to get a handle on generator commands. - # Command.instance('destroy', my_generator) instantiates a Destroy - # delegate of my_generator ready to do your dirty work. - def self.instance(command, generator) - const_get(command.to_s.camelize).new(generator) - end - - # Even more convenient access to commands. Include Commands in - # the generator Base class to get a nice #command instance method - # which returns a delegate for the requested command. - def self.included(base) - base.send(:define_method, :command) do |command| - Commands.instance(command, self) - end - end - - - # Generator commands delegate Rails::Generator::Base and implement - # a standard set of actions. Their behavior is defined by the way - # they respond to these actions: Create brings life; Destroy brings - # death; List passively observes. - # - # Commands are invoked by replaying (or rewinding) the generator's - # manifest of actions. See Rails::Generator::Manifest and - # Rails::Generator::Base#manifest method that generator subclasses - # are required to override. - # - # Commands allows generators to "plug in" invocation behavior, which - # corresponds to the GoF Strategy pattern. - class Base < DelegateClass(Rails::Generator::Base) - # Replay action manifest. RewindBase subclass rewinds manifest. - def invoke! - manifest.replay(self) - after_generate - end - - def dependency(generator_name, args, runtime_options = {}) - logger.dependency(generator_name) do - self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke! - end - end - - # Does nothing for all commands except Create. - def class_collisions(*class_names) - end - - # Does nothing for all commands except Create. - def readme(*args) - end - - protected - def current_migration_number - Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path| - n = File.basename(file_path).split('_', 2).first.to_i - if n > max then n else max end - end - end - - def next_migration_number - current_migration_number + 1 - end - - def migration_directory(relative_path) - directory(@migration_directory = relative_path) - end - - def existing_migrations(file_name) - Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/) - end - - def migration_exists?(file_name) - not existing_migrations(file_name).empty? - end - - def next_migration_string(padding = 3) - if ActiveRecord::Base.timestamped_migrations - Time.now.utc.strftime("%Y%m%d%H%M%S") - else - "%.#{padding}d" % next_migration_number - end - end - - def gsub_file(relative_destination, regexp, *args, &block) - path = destination_path(relative_destination) - content = File.read(path).gsub(regexp, *args, &block) - File.open(path, 'wb') { |file| file.write(content) } - end - - private - # Ask the user interactively whether to force collision. - def force_file_collision?(destination, src, dst, file_options = {}, &block) - $stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] " - case $stdin.gets.chomp - when /\Ad\z/i - Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp| - temp.write render_file(src, file_options, &block) - temp.rewind - $stdout.puts `#{diff_cmd} "#{dst}" "#{temp.path}"` - end - puts "retrying" - raise 'retry diff' - when /\Aa\z/i - $stdout.puts "forcing #{spec.name}" - options[:collision] = :force - when /\Aq\z/i - $stdout.puts "aborting #{spec.name}" - raise SystemExit - when /\An\z/i then :skip - when /\Ay\z/i then :force - else - $stdout.puts <<-HELP -Y - yes, overwrite -n - no, do not overwrite -a - all, overwrite this and all others -q - quit, abort -d - diff, show the differences between the old and the new -h - help, show this help -HELP - raise 'retry' - end - rescue - retry - end - - def diff_cmd - ENV['RAILS_DIFF'] || 'diff -u' - end - - def render_template_part(template_options) - # Getting Sandbox to evaluate part template in it - part_binding = template_options[:sandbox].call.sandbox_binding - part_rel_path = template_options[:insert] - part_path = source_path(part_rel_path) - - # Render inner template within Sandbox binding - rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding) - begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id]) - end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id]) - begin_mark + rendered_part + end_mark - end - - def template_part_mark(name, id) - "<!--[#{name}:#{id}]-->\n" - end - end - - # Base class for commands which handle generator actions in reverse, such as Destroy. - class RewindBase < Base - # Rewind action manifest. - def invoke! - manifest.rewind(self) - end - end - - - # Create is the premier generator command. It copies files, creates - # directories, renders templates, and more. - class Create < Base - - # Check whether the given class names are already taken by - # Ruby or Rails. In the future, expand to check other namespaces - # such as the rest of the user's app. - def class_collisions(*class_names) - path = class_names.shift - class_names.flatten.each do |class_name| - # Convert to string to allow symbol arguments. - class_name = class_name.to_s - - # Skip empty strings. - next if class_name.strip.empty? - - # Split the class from its module nesting. - nesting = class_name.split('::') - name = nesting.pop - - # Hack to limit const_defined? to non-inherited on 1.9. - extra = [] - extra << false unless Object.method(:const_defined?).arity == 1 - - # Extract the last Module in the nesting. - last = nesting.inject(Object) { |last, nest| - break unless last.const_defined?(nest, *extra) - last.const_get(nest) - } - - # If the last Module exists, check whether the given - # class exists and raise a collision if so. - if last and last.const_defined?(name.camelize, *extra) - raise_class_collision(class_name) - end - end - end - - # 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. - def file(relative_source, relative_destination, file_options = {}, &block) - # Determine full paths for source and destination files. - source = source_path(relative_source) - destination = destination_path(relative_destination) - destination_exists = File.exist?(destination) - - # If source and destination are identical then we're done. - if destination_exists and identical?(source, destination, &block) - return logger.identical(relative_destination) - end - - # Check for and resolve file collisions. - if destination_exists - - # Make a choice whether to overwrite the file. :force and - # :skip already have their mind made up, but give :ask a shot. - choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask - when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block) - when :force then :force - when :skip then :skip - else raise "Invalid collision option: #{options[:collision].inspect}" - end - - # Take action based on our choice. Bail out if we chose to - # skip the file; otherwise, log our transgression and continue. - case choice - when :force then logger.force(relative_destination) - when :skip then return(logger.skip(relative_destination)) - else raise "Invalid collision choice: #{choice}.inspect" - end - - # File doesn't exist so log its unbesmirched creation. - else - logger.create relative_destination - end - - # If we're pretending, back off now. - return if options[:pretend] - - # Write destination file with optional shebang. Yield for content - # if block given so templaters may render the source file. If a - # shebang is requested, replace the existing shebang or insert a - # new one. - File.open(destination, 'wb') do |dest| - dest.write render_file(source, file_options, &block) - end - - # Optionally change permissions. - if file_options[:chmod] - FileUtils.chmod(file_options[:chmod], destination) - end - - # Optionally add file to subversion or git - system("svn add #{destination}") if options[:svn] - system("git add -v #{relative_destination}") if options[:git] - end - - # 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. - def identical?(source, destination, &block) - return false if File.directory? destination - source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source) - destination = IO.read(destination) - source == destination - end - - # Generate a file for a Rails application 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. - def template(relative_source, relative_destination, template_options = {}) - file(relative_source, relative_destination, template_options) do |file| - # Evaluate any assignments in a temporary, throwaway binding. - vars = template_options[:assigns] || {} - b = template_options[:binding] || binding - vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b } - - # Render the source file with the temporary binding. - ERB.new(file.read, nil, '-').result(b) - end - end - - def complex_template(relative_source, relative_destination, template_options = {}) - options = template_options.dup - options[:assigns] ||= {} - options[:assigns]['template_for_inclusion'] = render_template_part(template_options) - template(relative_source, relative_destination, options) - end - - # Create a directory including any missing parent directories. - # Always skips directories which exist. - def directory(relative_path) - path = destination_path(relative_path) - if File.exist?(path) - logger.exists relative_path - else - logger.create relative_path - unless options[:pretend] - FileUtils.mkdir_p(path) - # git doesn't require adding the paths, adding the files later will - # automatically do a path add. - - # Subversion doesn't do path adds, so we need to add - # each directory individually. - # So stack up the directory tree and add the paths to - # subversion in order without recursion. - if options[:svn] - stack = [relative_path] - until File.dirname(stack.last) == stack.last # dirname('.') == '.' - stack.push File.dirname(stack.last) - end - stack.reverse_each do |rel_path| - svn_path = destination_path(rel_path) - system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn')) - end - end - end - end - end - - # Display a README. - def readme(*relative_sources) - relative_sources.flatten.each do |relative_source| - logger.readme relative_source - puts File.read(source_path(relative_source)) unless options[:pretend] - end - end - - # When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template. - def migration_template(relative_source, relative_destination, template_options = {}) - migration_directory relative_destination - migration_file_name = template_options[:migration_file_name] || file_name - raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name) - template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options) - end - - def route_resources(*resources) - resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') - sentinel = 'ActionController::Routing::Routes.draw do |map|' - - logger.route "map.resources #{resource_list}" - unless options[:pretend] - gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| - "#{match}\n map.resources #{resource_list}\n" - end - end - end - - private - def render_file(path, options = {}) - File.open(path, 'rb') do |file| - if block_given? - yield file - else - content = '' - if shebang = options[:shebang] - content << "#!#{shebang}\n" - if line = file.gets - content << "line\n" if line !~ /^#!/ - end - end - content << file.read - end - end - end - - # Raise a usage error with an informative WordNet suggestion. - # Thanks to Florian Gross (flgr). - def raise_class_collision(class_name) - message = <<end_message - The name '#{class_name}' is either already used in your application or reserved by Ruby on Rails. - Please choose an alternative and run this generator again. -end_message - if suggest = find_synonyms(class_name) - if suggest.any? - message << "\n Suggestions: \n\n" - message << suggest.join("\n") - end - end - raise UsageError, message - end - - SYNONYM_LOOKUP_URI = "http://wordnet.princeton.edu/perl/webwn?s=%s" - - # Look up synonyms on WordNet. Thanks to Florian Gross (flgr). - def find_synonyms(word) - require 'open-uri' - require 'timeout' - timeout(5) do - open(SYNONYM_LOOKUP_URI % word) do |stream| - # Grab words linked to dictionary entries as possible synonyms - data = stream.read.gsub(" ", " ").scan(/<a href="webwn.*?">([\w ]*?)<\/a>/s).uniq - end - end - rescue Exception - return nil - end - end - - - # Undo the actions performed by a generator. Rewind the action - # manifest and attempt to completely erase the results of each action. - class Destroy < RewindBase - # Remove a file if it exists and is a file. - def file(relative_source, relative_destination, file_options = {}) - destination = destination_path(relative_destination) - if File.exist?(destination) - logger.rm relative_destination - unless options[:pretend] - if options[:svn] - # If the file has been marked to be added - # but has not yet been checked in, revert and delete - if options[:svn][relative_destination] - system("svn revert #{destination}") - FileUtils.rm(destination) - else - # If the directory is not in the status list, it - # has no modifications so we can simply remove it - system("svn rm #{destination}") - end - elsif options[:git] - if options[:git][:new][relative_destination] - # file has been added, but not committed - system("git reset HEAD #{relative_destination}") - FileUtils.rm(destination) - elsif options[:git][:modified][relative_destination] - # file is committed and modified - system("git rm -f #{relative_destination}") - else - # If the directory is not in the status list, it - # has no modifications so we can simply remove it - system("git rm #{relative_destination}") - end - else - FileUtils.rm(destination) - end - end - else - logger.missing relative_destination - return - end - end - - # Templates are deleted just like files and the actions take the - # same parameters, so simply alias the file method. - alias_method :template, :file - - # Remove each directory in the given path from right to left. - # Remove each subdirectory if it exists and is a directory. - def directory(relative_path) - parts = relative_path.split('/') - until parts.empty? - partial = File.join(parts) - path = destination_path(partial) - if File.exist?(path) - if Dir[File.join(path, '*')].empty? - logger.rmdir partial - unless options[:pretend] - if options[:svn] - # If the directory has been marked to be added - # but has not yet been checked in, revert and delete - if options[:svn][relative_path] - system("svn revert #{path}") - FileUtils.rmdir(path) - else - # If the directory is not in the status list, it - # has no modifications so we can simply remove it - system("svn rm #{path}") - end - # I don't think git needs to remove directories?.. - # or maybe they have special consideration... - else - FileUtils.rmdir(path) - end - end - else - logger.notempty partial - end - else - logger.missing partial - end - parts.pop - end - end - - def complex_template(*args) - # nothing should be done here - end - - # When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}". - def migration_template(relative_source, relative_destination, template_options = {}) - migration_directory relative_destination - - migration_file_name = template_options[:migration_file_name] || file_name - unless migration_exists?(migration_file_name) - puts "There is no migration named #{migration_file_name}" - return - end - - - existing_migrations(migration_file_name).each do |file_path| - file(relative_source, file_path, template_options) - end - end - - def route_resources(*resources) - resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') - look_for = "\n map.resources #{resource_list}\n" - logger.route "map.resources #{resource_list}" - gsub_file 'config/routes.rb', /(#{look_for})/mi, '' - end - end - - - # List a generator's action manifest. - class List < Base - def dependency(generator_name, args, options = {}) - logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})" - end - - def class_collisions(*class_names) - logger.class_collisions class_names.join(', ') - end - - def file(relative_source, relative_destination, options = {}) - logger.file relative_destination - end - - def template(relative_source, relative_destination, options = {}) - logger.template relative_destination - end - - def complex_template(relative_source, relative_destination, options = {}) - logger.template "#{options[:insert]} inside #{relative_destination}" - end - - def directory(relative_path) - logger.directory "#{destination_path(relative_path)}/" - end - - def readme(*args) - logger.readme args.join(', ') - end - - def migration_template(relative_source, relative_destination, options = {}) - migration_directory relative_destination - logger.migration_template file_name - end - - def route_resources(*resources) - resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') - logger.route "map.resources #{resource_list}" - end - end - - # Update generator's action manifest. - class Update < Create - def file(relative_source, relative_destination, options = {}) - # logger.file relative_destination - end - - def template(relative_source, relative_destination, options = {}) - # logger.template relative_destination - end - - def complex_template(relative_source, relative_destination, template_options = {}) - - begin - dest_file = destination_path(relative_destination) - source_to_update = File.readlines(dest_file).join - rescue Errno::ENOENT - logger.missing relative_destination - return - end - - logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}" - - begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id])) - end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id])) - - # Refreshing inner part of the template with freshly rendered part. - rendered_part = render_template_part(template_options) - source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part) - - File.open(dest_file, 'w') { |file| file.write(source_to_update) } - end - - def directory(relative_path) - # logger.directory "#{destination_path(relative_path)}/" - end - end - - end - end -end |