module Rails::Generators

def self.aliases #:nodoc:

:nodoc:
def self.aliases #:nodoc:
  @aliases ||= DEFAULT_ALIASES.dup
end

def self.api_only!

so generators such as scaffold won't create them.
migration generators, and completely disable helpers and assets
everything that is usually browser related, such as assets and session
Configure generators for API only applications. It basically hides
def self.api_only!
  hide_namespaces "assets", "helper", "css", "js"
  options[:rails].merge!(
    api: true,
    assets: false,
    helper: false,
    template_engine: nil
  )
  if ARGV.first == 'mailer'
    options[:rails].merge!(template_engine: :erb)
  end
end

def self.configure!(config) #:nodoc:

:nodoc:
def self.configure!(config) #:nodoc:
  api_only! if config.api_only
  no_color! unless config.colorize_logging
  aliases.deep_merge! config.aliases
  options.deep_merge! config.options
  fallbacks.merge! config.fallbacks
  templates_path.concat config.templates
  templates_path.uniq!
  hide_namespaces(*config.hidden_namespaces)
end

def self.fallbacks

Rails::Generators.fallbacks[:shoulda] = :test_unit

some of them are not available by adding a fallback:
Shoulda then can tell generators to search for test_unit generators when

test_unit ones.
of test_unit. However, most part of shoulda generators are similar to
For example, shoulda is considered a test_framework and is an extension

they can add a fallback.
generator group to fallback to another group in case of missing generators,
Hold configured generators fallbacks. If a plugin developer wants a
def self.fallbacks
  @fallbacks ||= {}
end

def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:

:nodoc:
Rails looks for is the first and last parts of the namespace.
Notice that "rails:generators:webrat" could be loaded as well, what

"rails:webrat", "webrat:integration", "webrat"

Will search for the following generators:

find_by_namespace :webrat, :rails, :integration

looks in load paths and loads the generator just before it's going to be used.
Generators names must end with "_generator.rb". This is required because Rails

Rails finds namespaces similar to thor, it only adds one rule:
def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
  lookups = []
  lookups << "#{base}:#{name}"    if base
  lookups << "#{name}:#{context}" if context
  unless base || context
    unless name.to_s.include?(?:)
      lookups << "#{name}:#{name}"
      lookups << "rails:#{name}"
    end
    lookups << "#{name}"
  end
  lookup(lookups)
  namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
  lookups.each do |namespace|
    klass = namespaces[namespace]
    return klass if klass
  end
  invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
end

def self.help(command = 'generate')

Show help message with available generators.
def self.help(command = 'generate')
  puts "Usage: rails #{command} GENERATOR [args] [options]"
  puts
  puts "General options:"
  puts "  -h, [--help]     # Print generator's options and usage"
  puts "  -p, [--pretend]  # Run but do not make any changes"
  puts "  -f, [--force]    # Overwrite files that already exist"
  puts "  -s, [--skip]     # Skip files that already exist"
  puts "  -q, [--quiet]    # Suppress status output"
  puts
  puts "Please choose a generator below."
  puts
  print_generators
end

def self.hidden_namespaces

such as "css:scaffold".
invoked with the shorter "migration", others are private to other generators
Some are aliased such as "rails:migration" and can be
Generator namespaces may be hidden for a variety of reasons.
Returns an array of generator namespaces that are hidden.
def self.hidden_namespaces
  @hidden_namespaces ||= begin
    orm      = options[:rails][:orm]
    test     = options[:rails][:test_framework]
    template = options[:rails][:template_engine]
    css      = options[:rails][:stylesheet_engine]
    [
      "rails",
      "resource_route",
      "#{orm}:migration",
      "#{orm}:model",
      "#{test}:controller",
      "#{test}:helper",
      "#{test}:integration",
      "#{test}:mailer",
      "#{test}:model",
      "#{test}:scaffold",
      "#{test}:view",
      "#{test}:job",
      "#{template}:controller",
      "#{template}:scaffold",
      "#{template}:mailer",
      "#{css}:scaffold",
      "#{css}:assets",
      "css:assets",
      "css:scaffold"
    ]
  end
end

def self.invoke(namespace, args=ARGV, config={})

commands.
It's used as the default entry point for generate, destroy and update
Receives a namespace, arguments and the behavior to invoke the generator.
def self.invoke(namespace, args=ARGV, config={})
  names = namespace.to_s.split(':')
  if klass = find_by_namespace(names.pop, names.any? && names.join(':'))
    args << "--help" if args.empty? && klass.arguments.any?(&:required?)
    klass.start(args, config)
  else
    options     = sorted_groups.flat_map(&:last)
    suggestions = options.sort_by {|suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
    msg =  "Could not find generator '#{namespace}'. "
    msg << "Maybe you meant #{ suggestions.map {|s| "'#{s}'"}.to_sentence(last_word_connector: " or ", locale: :en) }\n"
    msg << "Run `rails generate --help` for more options."
    puts msg
  end
end

def self.invoke_fallbacks_for(name, base) #:nodoc:

:nodoc:
Try fallbacks for the given base.
def self.invoke_fallbacks_for(name, base) #:nodoc:
  return nil unless base && fallbacks[base.to_sym]
  invoked_fallbacks = []
  Array(fallbacks[base.to_sym]).each do |fallback|
    next if invoked_fallbacks.include?(fallback)
    invoked_fallbacks << fallback
    klass = find_by_namespace(name, fallback)
    return klass if klass
  end
  nil
end

def self.levenshtein_distance str1, str2

Returns a value representing the "cost" of transforming str1 into str2
This code is based directly on the Text gem implementation
def self.levenshtein_distance str1, str2
  s = str1
  t = str2
  n = s.length
  m = t.length
  return m if (0 == n)
  return n if (0 == m)
  d = (0..m).to_a
  x = nil
  # avoid duplicating an enumerable object in the loop
  str2_codepoint_enumerable = str2.each_codepoint
  str1.each_codepoint.with_index do |char1, i|
    e = i+1
    str2_codepoint_enumerable.with_index do |char2, j|
      cost = (char1 == char2) ? 0 : 1
      x = [
           d[j+1] + 1, # insertion
           e + 1,      # deletion
           d[j] + cost # substitution
          ].min
      d[j] = e
      e = x
    end
    d[m] = x
  end
  x
end

def self.lookup(namespaces) #:nodoc:

:nodoc:
in the load path.
Receives namespaces in an array and tries to find matching generators
def self.lookup(namespaces) #:nodoc:
  paths = namespaces_to_paths(namespaces)
  paths.each do |raw_path|
    ["rails/generators", "generators"].each do |base|
      path = "#{base}/#{raw_path}_generator"
      begin
        require path
        return
      rescue LoadError => e
        raise unless e.message =~ /#{Regexp.escape(path)}$/
      rescue Exception => e
        warn "[WARNING] Could not load generator #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
      end
    end
  end
end

def self.lookup! #:nodoc:

:nodoc:
This will try to load any generator in the load path to show in help.
def self.lookup! #:nodoc:
  $LOAD_PATH.each do |base|
    Dir[File.join(base, "{rails/generators,generators}", "**", "*_generator.rb")].each do |path|
      begin
        path = path.sub("#{base}/", "")
        require path
      rescue Exception
        # No problem
      end
    end
  end
end

def self.namespaces_to_paths(namespaces) #:nodoc:

:nodoc:
in both: "rails/model/model_generator" and "rails/model_generator".
an extra lookup. For example, "rails:model" should be searched
Convert namespaces to paths by replacing ":" for "/" and adding
def self.namespaces_to_paths(namespaces) #:nodoc:
  paths = []
  namespaces.each do |namespace|
    pieces = namespace.split(":")
    paths << pieces.dup.push(pieces.last).join("/")
    paths << pieces.join("/")
  end
  paths.uniq!
  paths
end

def self.no_color!

Remove the color from output.
def self.no_color!
  Thor::Base.shell = Thor::Shell::Basic
end

def self.options #:nodoc:

:nodoc:
def self.options #:nodoc:
  @options ||= DEFAULT_OPTIONS.dup
end

def self.print_generators

def self.print_generators
  sorted_groups.each { |b, n| print_list(b, n) }
end

def self.print_list(base, namespaces) #:nodoc:

:nodoc:
Prints a list of generators.
def self.print_list(base, namespaces) #:nodoc:
  namespaces = namespaces.reject do |n|
    hidden_namespaces.include?(n)
  end
  return if namespaces.empty?
  puts "#{base.camelize}:"
  namespaces.each do |namespace|
    puts("  #{namespace}")
  end
  puts
end

def self.public_namespaces

def self.public_namespaces
  lookup!
  subclasses.map(&:namespace)
end

def self.sorted_groups

def self.sorted_groups
  namespaces = public_namespaces
  namespaces.sort!
  groups = Hash.new { |h,k| h[k] = [] }
  namespaces.each do |namespace|
    base = namespace.split(':').first
    groups[base] << namespace
  end
  rails = groups.delete("rails")
  rails.map! { |n| n.sub(/^rails:/, '') }
  rails.delete("app")
  rails.delete("plugin")
  hidden_namespaces.each { |n| groups.delete(n.to_s) }
  [["rails", rails]] + groups.sort.to_a
end

def self.subclasses

Track all generators subclasses.
def self.subclasses
  @subclasses ||= []
end

def self.templates_path #:nodoc:

:nodoc:
def self.templates_path #:nodoc:
  @templates_path ||= []
end

def hide_namespaces(*namespaces)

def hide_namespaces(*namespaces)
  hidden_namespaces.concat(namespaces)
end