aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/generators.rb
diff options
context:
space:
mode:
Diffstat (limited to 'railties/lib/rails/generators.rb')
-rw-r--r--railties/lib/rails/generators.rb150
1 files changed, 57 insertions, 93 deletions
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 2ba56bc3c5..3713a38b33 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -10,7 +10,7 @@ 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.1/lib")
+$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.3/lib")
require 'rails/generators/base'
require 'rails/generators/named_base'
@@ -117,11 +117,15 @@ module Rails
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
+
# Generators load paths used on lookup. The lookup happens as:
#
# 1) lib generators
@@ -147,18 +151,10 @@ module Rails
end
load_paths # Cache load paths. Needed to avoid __FILE__ pointing to wrong paths.
- # Rails finds namespaces exactly as thor, with three conveniences:
- #
- # 1) If your generator name ends with generator, as WebratGenerator, it sets
- # its namespace to "webrat", so it can be invoked as "webrat" and not
- # "webrat_generator";
+ # Rails finds namespaces similar to thor, it only adds one rule:
#
- # 2) If your generator has a generators namespace, as Rails::Generators::WebratGenerator,
- # the namespace is set to "rails:generators:webrat", but Rails allows it
- # to be invoked simply as "rails:webrat". The "generators" is added
- # automatically when doing the lookup;
- #
- # 3) Rails 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
+ # looks in load paths and loads the generator just before it's going to be used.
#
# ==== Examples
#
@@ -166,113 +162,81 @@ module Rails
#
# Will search for the following generators:
#
- # "rails:generators:webrat", "webrat:generators:integration", "webrat"
- #
- # On the other hand, if "rails:webrat" is given, it will search for:
+ # "rails:webrat", "webrat:integration", "webrat"
#
- # "rails:generators:webrat", "rails:webrat"
- #
- # Notice that the "generators" namespace is handled automatically by Rails,
- # so you don't need to type it when you want to invoke a generator in specific.
+ # 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:
- name, attempts = name.to_s, [ ]
-
- case name.count(':')
- when 1
- base, name = name.split(':')
- return find_by_namespace(name, base)
- when 0
- attempts += generator_names(base, name) if base
- attempts += generator_names(name, context) if context
- end
-
- attempts << name
- attempts += generator_names(name, name) unless name.include?(?:)
- attempts.uniq!
-
- unloaded = attempts - namespaces
- lookup(unloaded)
+ # Mount regexps to lookup
+ regexps = []
+ regexps << /^#{base}:[\w:]*#{name}$/ if base
+ regexps << /^#{name}:[\w:]*#{context}$/ if context
+ regexps << /^[(#{name}):]+$/
+ regexps.uniq!
+
+ # Check if generator happens to be loaded
+ checked = subclasses.dup
+ klass = find_by_regexps(regexps, checked)
+ return klass if klass
+
+ # Try to require other generators by looking in load_paths
+ lookup(name, context)
+ unchecked = subclasses - checked
+ klass = find_by_regexps(regexps, unchecked)
+ return klass if klass
+
+ # Invoke fallbacks
+ invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
+ end
- attempts.each do |namespace|
- klass = Thor::Util.find_by_namespace(namespace)
- return klass if klass
+ # Tries to find a generator which the namespace match the regexp.
+ def self.find_by_regexps(regexps, klasses)
+ klasses.find do |klass|
+ namespace = klass.namespace
+ regexps.find { |r| namespace =~ r }
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={})
- if klass = find_by_namespace(namespace, "rails")
+ names = namespace.to_s.split(':')
+
+ if klass = find_by_namespace(names.pop, names.shift || "rails")
args << "--help" if klass.arguments.any? { |a| a.required? } && args.empty?
- klass.start args, config
+ klass.start(args, config)
else
puts "Could not find generator #{namespace}."
end
end
# Show help message with available generators.
- #
def self.help
- rails = Rails::Generators.builtin.map do |group, name|
- name if group == "rails"
- end
- rails.compact!
- rails.sort!
-
- puts "Please select a generator."
- puts "Builtin: #{rails.join(', ')}."
-
- # Load paths and remove builtin
- paths, others = load_paths.dup, []
- paths.pop
-
- paths.each do |path|
- tail = [ "*", "*", "*_generator.rb" ]
-
- until tail.empty?
- others += Dir[File.join(path, *tail)].collect do |file|
- name = file.split('/')[-tail.size, 2]
- name.last.sub!(/_generator\.rb$/, '')
- name.uniq!
- name.join(':')
- end
- tail.shift
- end
- end
+ builtin = Rails::Generators.builtin.each { |n| n.sub!(/^rails:/, '') }
+ builtin.sort!
+ lookup("*")
+ others = subclasses.map{ |k| k.namespace.gsub(':generators:', ':') }
+ others -= Rails::Generators.builtin
others.sort!
+
+ puts "Please select a generator."
+ puts "Builtin: #{builtin.join(', ')}."
puts "Others: #{others.join(', ')}." unless others.empty?
end
protected
- # Return all defined namespaces.
- #
- def self.namespaces #:nodoc:
- Thor::Base.subclasses.map { |klass| klass.namespace }
- end
-
- # Keep builtin generators in an Array[Array[group, name]].
- #
+ # Keep builtin generators in an Array.
def self.builtin #:nodoc:
Dir[File.dirname(__FILE__) + '/generators/*/*'].collect do |file|
- file.split('/')[-2, 2]
+ file.split('/')[-2, 2].join(':')
end
end
- # By default, Rails strips the generator namespace to make invocations
- # easier. This method generaters the both possibilities names.
- def self.generator_names(first, second) #:nodoc:
- [ "#{first}:generators:#{second}", "#{first}:#{second}" ]
- end
-
- # Try callbacks for the given base.
- #
+ # Try fallbacks for the given base.
def self.invoke_fallbacks_for(name, base) #:nodoc:
return nil unless base && fallbacks[base.to_sym]
invoked_fallbacks = []
@@ -290,10 +254,10 @@ module Rails
# Receives namespaces in an array and tries to find matching generators
# in the load path.
- #
- def self.lookup(attempts) #:nodoc:
- attempts = attempts.map { |a| "#{a.split(":").last}_generator" }.uniq
- attempts = "{#{attempts.join(',')}}.rb"
+ def self.lookup(*attempts) #:nodoc:
+ attempts.compact!
+ attempts.uniq!
+ attempts = "{#{attempts.join(',')}}_generator.rb"
self.load_paths.each do |path|
Dir[File.join(path, '**', attempts)].each do |file|