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
|