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.rb218
1 files changed, 66 insertions, 152 deletions
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 6a112fc710..67037106e5 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -2,6 +2,7 @@ activesupport_path = File.expand_path("../../../../activesupport/lib", __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require "thor/group"
+require "rails/command"
require "active_support"
require "active_support/core_ext/object/blank"
@@ -13,6 +14,8 @@ require "active_support/core_ext/string/inflections"
module Rails
module Generators
+ include Rails::Command::Behavior
+
autoload :Actions, "rails/generators/actions"
autoload :ActiveModel, "rails/generators/active_model"
autoload :Base, "rails/generators/base"
@@ -127,67 +130,6 @@ module Rails
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.
- #
- # 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 = 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
-
- # 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.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
-
# Returns an array of generator namespaces that are hidden.
# Generator namespaces may be hidden for a variety of reasons.
# Some are aliased such as "rails:migration" and can be
@@ -260,11 +202,13 @@ module Rails
def self.sorted_groups
namespaces = public_namespaces
namespaces.sort!
- groups = Hash.new { |h,k| h[k] = [] }
+
+ 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")
@@ -272,62 +216,69 @@ module Rails
hidden_namespaces.each { |n| groups.delete(n.to_s) }
- [["rails", rails]] + groups.sort.to_a
+ [[ "rails", rails ]] + groups.sort.to_a
end
- protected
+ # 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.
+ #
+ # 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
- # This code is based directly on the Text gem implementation
- # Returns a value representing the "cost" of transforming str1 into str2
- 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
+ unless base || context
+ unless name.to_s.include?(?:)
+ lookups << "#{name}:#{name}"
+ lookups << "rails:#{name}"
end
-
- x
+ lookups << "#{name}"
end
- # Prints a list of generators.
- def self.print_list(base, namespaces) #:nodoc:
- namespaces = namespaces.reject do |n|
- hidden_namespaces.include?(n)
- end
+ lookup(lookups)
- return if namespaces.empty?
- puts "#{base.camelize}:"
+ namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
+ lookups.each do |namespace|
- namespaces.each do |namespace|
- puts(" #{namespace}")
- end
+ 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.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
- puts
+ protected
+ def self.print_list(base, namespaces)
+ namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
+ super
end
# Try fallbacks for the given base.
@@ -346,53 +297,16 @@ module Rails
nil
end
- # Receives namespaces in an array and tries to find matching generators
- # in the load path.
- 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
+ def self.command_type
+ @command_type ||= "generator"
end
- # 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
+ def self.lookup_paths
+ @lookup_paths ||= %w( rails/generators 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("/")
- paths << pieces.join("/")
- end
- paths.uniq!
- paths
+ def self.file_lookup_paths
+ @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ]
end
end
end