activesupport_path = File.expand_path('../../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) require 'active_support' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/metaclass' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/string/inflections' # TODO: Do not always push on vendored thor $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.3/lib") require 'rails/generators/base' require 'rails/generators/named_base' module Rails module Generators DEFAULT_ALIASES = { :rails => { :actions => '-a', :orm => '-o', :resource_controller => '-c', :scaffold_controller => '-c', :stylesheets => '-y', :template_engine => '-e', :test_framework => '-t' }, :test_unit => { :fixture_replacement => '-r', }, :plugin => { :generator => '-g', :tasks => '-r' } } DEFAULT_OPTIONS = { :active_record => { :migration => true, :timestamps => true }, :erb => { :layout => true }, :rails => { :force_plural => false, :helper => true, :layout => true, :orm => :active_record, :integration_tool => :test_unit, :performance_tool => :test_unit, :resource_controller => :controller, :scaffold_controller => :scaffold_controller, :singleton => false, :stylesheets => true, :template_engine => :erb, :test_framework => :test_unit }, :test_unit => { :fixture => true, :fixture_replacement => nil }, :plugin => { :generator => false, :tasks => false } } def self.configure!(config = Rails.application.config.generators) #:nodoc: no_color! unless config.colorize_logging aliases.deep_merge! config.aliases options.deep_merge! config.options end def self.aliases #:nodoc: @aliases ||= DEFAULT_ALIASES.dup end def self.options #:nodoc: @options ||= DEFAULT_OPTIONS.dup end # Hold configured generators fallbacks. If a plugin developer wants a # generator group to fallback to another group in case of missing generators, # they can add a fallback. # # For example, shoulda is considered a test_framework and is an extension # of test_unit. However, most part of shoulda generators are similar to # test_unit ones. # # Shoulda then can tell generators to search for test_unit generators when # some of them are not available by adding a fallback: # # Rails::Generators.fallbacks[:shoulda] = :test_unit # def self.fallbacks @fallbacks ||= {} end # Remove the color from output. def self.no_color! Thor::Base.shell = Thor::Shell::Basic end # Track all generators subclasses. def self.subclasses @subclasses ||= [] end # Rails finds namespaces similar to thor, it only adds one rule: # # Generators names must end with "_generator.rb". This is required because Rails # looks in load paths and loads the generator just before it's going to be used. # # ==== Examples # # find_by_namespace :webrat, :rails, :integration # # Will search for the following generators: # # "rails:webrat", "webrat:integration", "webrat" # # Notice that "rails:generators:webrat" could be loaded as well, what # Rails looks for is the first and last parts of the namespace. # 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 = subclasses.inject({}) do |hash, klass| hash[klass.namespace] = klass hash end lookups.each do |namespace| klass = namespaces[namespace] return klass if klass end invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name) end # Receives a namespace, arguments and the behavior to invoke the generator. # It's used as the default entry point for generate, destroy and update # commands. def self.invoke(namespace, args=ARGV, config={}) names = namespace.to_s.split(':') if klass = find_by_namespace(names.pop, names.shift) args << "--help" if args.empty? && klass.arguments.any? { |a| a.required? } klass.start(args, config) else puts "Could not find generator #{namespace}." end end # Show help message with available generators. def self.help lookup! namespaces = subclasses.map{ |k| k.namespace } namespaces.sort! groups = Hash.new { |h,k| h[k] = [] } namespaces.each do |namespace| base = namespace.split(':').first groups[base] << namespace end puts "Usage:" puts " script/generate GENERATOR [args] [options]" puts puts "General options:" puts " -h, [--help] # Print generators 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] # Supress status output" puts puts "Please choose a generator below." puts # Print Rails defaults first. rails = groups.delete("rails") rails.map! { |n| n.sub(/^rails:/, '') } print_list("rails", rails) groups.sort.each { |b, n| print_list(b, n) } end protected # Prints a list of generators. def self.print_list(base, namespaces) #:nodoc: return if namespaces.empty? puts "#{base.camelize}:" namespaces.each { |namespace| puts(" #{namespace}") } puts end # 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 # Receives namespaces in an array and tries to find matching generators # in the load path. def self.lookup(namespaces) #:nodoc: load_generators_from_railties! 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 NameError => e raise unless e.message =~ /Rails::Generator([\s(::)]|$)/ warn "[WARNING] Could not load generator #{path.inspect} because it's a Rails 2.x generator, which is not supported anymore. Error: #{e.message}.\n#{e.backtrace.join("\n")}" rescue Exception => e warn "[WARNING] Could not load generator #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}" end end end end # This will try to load any generator in the load path to show in help. def self.lookup! #:nodoc: load_generators_from_railties! $LOAD_PATH.each do |base| Dir[File.join(base, "{generators,rails_generators}", "**", "*_generator.rb")].each do |path| begin require path rescue Exception => e # No problem end end end end # Allow generators to be loaded from custom paths. def self.load_generators_from_railties! #:nodoc: return if defined?(@generators_from_railties) || Rails.application.nil? @generators_from_railties = true Rails.application.load_generators end # Convert namespaces to paths by replacing ":" for "/" and adding # an extra lookup. For example, "rails:model" should be searched # in both: "rails/model/model_generator" and "rails/model_generator". def self.namespaces_to_paths(namespaces) #:nodoc: paths = [] namespaces.each do |namespace| pieces = namespace.split(":") paths << pieces.dup.push(pieces.last).join("/") unless pieces.uniq.size == 1 paths << pieces.join("/") end paths.uniq! paths end end end