3
require File.dirname(__FILE__) + '/spec'
7
# Lookup missing generators using const_missing. This allows any
8
# generator to reference another without having to know its location:
9
# RubyGems, ~/.rails/generators, and RAILS_ROOT/generators.
10
def lookup_missing_generator(class_id)
11
if md = /(.+)Generator$/.match(class_id.to_s)
12
name = md.captures.first.demodulize.underscore
13
Rails::Generator::Base.lookup(name).klass
15
const_missing_before_generators(class_id)
19
unless respond_to?(:const_missing_before_generators)
20
alias_method :const_missing_before_generators, :const_missing
21
alias_method :const_missing, :lookup_missing_generator
26
# User home directory lookup adapted from RubyGems.
30
elsif ENV['USERPROFILE']
32
elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
33
"#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
43
# Generator lookup is managed by a list of sources which return specs
44
# describing where to find and how to create generators. This module
45
# provides class methods for manipulating the source list and looking up
46
# generator specs, and an #instance wrapper for quickly instantiating
49
# A spec is not a generator: it's a description of where to find
50
# the generator and how to create it. A source is anything that
51
# yields generators from #each. PathSource and GemGeneratorSource are provided.
53
def self.included(base)
54
base.extend(ClassMethods)
55
base.use_component_sources!
58
# Convenience method to instantiate another generator.
59
def instance(generator_name, args, runtime_options = {})
60
self.class.instance(generator_name, args, runtime_options)
64
# The list of sources where we look, in order, for generators.
66
read_inheritable_attribute(:sources) or use_component_sources!
69
# Add a source to the end of the list.
70
def append_sources(*args)
71
sources.concat(args.flatten)
75
# Add a source to the beginning of the list.
76
def prepend_sources(*args)
77
write_inheritable_array(:sources, args.flatten + sources)
81
# Reset the source list.
83
write_inheritable_attribute(:sources, [])
87
# Use application generators (app, ?).
88
def use_application_sources!
90
sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
93
# Use component generators (model, controller, etc).
94
# 1. Rails application. If RAILS_ROOT is defined we know we're
95
# generating in the context of a Rails application, so search
96
# RAILS_ROOT/generators.
97
# 2. Look in plugins, either for generators/ or rails_generators/
98
# directories within each plugin
99
# 3. User home directory. Search ~/.rails/generators.
100
# 4. RubyGems. Search for gems named *_generator, and look for
101
# generators within any RubyGem's
102
# /rails_generators/<generator_name>_generator.rb file.
103
# 5. Builtins. Model, controller, mailer, scaffold, and so on.
104
def use_component_sources!
106
if defined? ::RAILS_ROOT
107
sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators")
108
sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators")
109
Rails.configuration.plugin_paths.each do |path|
110
relative_path = Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(::RAILS_ROOT))
111
sources << PathSource.new(:"plugins (#{relative_path})", "#{path}/*/**/{,rails_}generators")
114
sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
115
if Object.const_defined?(:Gem)
116
sources << GemGeneratorSource.new
117
sources << GemPathSource.new
119
sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
122
# Lookup knows how to find generators' Specs from a list of Sources.
123
# Searches the sources, in order, for the first matching name.
124
def lookup(generator_name)
126
generator_name = generator_name.to_s.downcase
127
@found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
128
unless @found[generator_name]
129
chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
131
gns = cache.select{|spec| spec.name =~ rx }
132
@found[generator_name] ||= gns.first if gns.length == 1
133
raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
135
@found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
138
# Convenience method to lookup and instantiate a generator.
139
def instance(generator_name, args = [], runtime_options = {})
140
lookup(generator_name).klass.new(args, full_options(runtime_options))
144
# Lookup and cache every generator from the source list.
146
@cache ||= sources.inject([]) { |cache, source| cache + source.to_a }
149
# Clear the cache whenever the source list changes.
150
def invalidate_cache!
156
# Sources enumerate (yield from #each) generator specs which describe
157
# where to find and how to create generators. Enumerable is mixed in so,
158
# for example, source.collect will retrieve every generator.
159
# Sources may be assigned a label to distinguish them.
164
def initialize(label)
168
# The each method must be implemented in subclasses.
169
# The base implementation raises an error.
171
raise NotImplementedError
174
# Return a convenient sorted list of all generator names.
176
map { |spec| spec.name }.sort
181
# PathSource looks for generators in a filesystem directory.
182
class PathSource < Source
185
def initialize(label, path)
190
# Yield each eligible subdirectory.
192
Dir["#{path}/[a-z]*"].each do |dir|
193
if File.directory?(dir)
194
yield Spec.new(File.basename(dir), dir, label)
200
class AbstractGemSource < Source
206
# GemGeneratorSource hits the mines to quarry for generators. The latest versions
207
# of gems named *_generator are selected.
208
class GemGeneratorSource < AbstractGemSource
209
# Yield latest versions of generator gems.
211
dependency = Gem::Dependency.new(/_generator$/, Gem::Requirement.default)
212
Gem::cache.search(dependency).inject({}) { |latest, gem|
213
hem = latest[gem.name]
214
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
216
}.values.each { |gem|
217
yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
222
# GemPathSource looks for generators within any RubyGem's /rails_generators/<generator_name>_generator.rb file.
223
class GemPathSource < AbstractGemSource
224
# Yield each generator within rails_generator subdirectories.
226
generator_full_paths.each do |generator|
227
yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label)
232
def generator_full_paths
233
@generator_full_paths ||=
234
Gem::cache.inject({}) do |latest, name_gem|
236
hem = latest[gem.name]
237
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
239
end.values.inject([]) do |mem, gem|
240
Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator|