~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/railties/lib/rails_generator/lookup.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
require 'pathname'
2
 
 
3
 
require File.dirname(__FILE__) + '/spec'
4
 
 
5
 
class Object
6
 
  class << self
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
14
 
      else
15
 
        const_missing_before_generators(class_id)
16
 
      end
17
 
    end
18
 
 
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
22
 
    end
23
 
  end
24
 
end
25
 
 
26
 
# User home directory lookup adapted from RubyGems.
27
 
def Dir.user_home
28
 
  if ENV['HOME']
29
 
    ENV['HOME']
30
 
  elsif ENV['USERPROFILE']
31
 
    ENV['USERPROFILE']
32
 
  elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
33
 
    "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
34
 
  else
35
 
    File.expand_path '~'
36
 
  end
37
 
end
38
 
 
39
 
 
40
 
module Rails
41
 
  module Generator
42
 
 
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
47
 
    # generators by name.
48
 
    #
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.
52
 
    module Lookup
53
 
      def self.included(base)
54
 
        base.extend(ClassMethods)
55
 
        base.use_component_sources!
56
 
      end
57
 
 
58
 
      # Convenience method to instantiate another generator.
59
 
      def instance(generator_name, args, runtime_options = {})
60
 
        self.class.instance(generator_name, args, runtime_options)
61
 
      end
62
 
 
63
 
      module ClassMethods
64
 
        # The list of sources where we look, in order, for generators.
65
 
        def sources
66
 
          read_inheritable_attribute(:sources) or use_component_sources!
67
 
        end
68
 
 
69
 
        # Add a source to the end of the list.
70
 
        def append_sources(*args)
71
 
          sources.concat(args.flatten)
72
 
          invalidate_cache!
73
 
        end
74
 
 
75
 
        # Add a source to the beginning of the list.
76
 
        def prepend_sources(*args)
77
 
          write_inheritable_array(:sources, args.flatten + sources)
78
 
          invalidate_cache!
79
 
        end
80
 
 
81
 
        # Reset the source list.
82
 
        def reset_sources
83
 
          write_inheritable_attribute(:sources, [])
84
 
          invalidate_cache!
85
 
        end
86
 
 
87
 
        # Use application generators (app, ?).
88
 
        def use_application_sources!
89
 
          reset_sources
90
 
          sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
91
 
        end
92
 
 
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!
105
 
          reset_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")
112
 
            end
113
 
          end
114
 
          sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
115
 
          if Object.const_defined?(:Gem)
116
 
            sources << GemGeneratorSource.new
117
 
            sources << GemPathSource.new
118
 
          end
119
 
          sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
120
 
        end
121
 
 
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)
125
 
          @found ||= {}
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}.*?"}
130
 
            rx = /^#{chars}$/
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
134
 
          end
135
 
          @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
136
 
        end
137
 
 
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))
141
 
        end
142
 
 
143
 
        private
144
 
          # Lookup and cache every generator from the source list.
145
 
          def cache
146
 
            @cache ||= sources.inject([]) { |cache, source| cache + source.to_a }
147
 
          end
148
 
 
149
 
          # Clear the cache whenever the source list changes.
150
 
          def invalidate_cache!
151
 
            @cache = nil
152
 
          end
153
 
      end
154
 
    end
155
 
 
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.
160
 
    class Source
161
 
      include Enumerable
162
 
 
163
 
      attr_reader :label
164
 
      def initialize(label)
165
 
        @label = label
166
 
      end
167
 
 
168
 
      # The each method must be implemented in subclasses.
169
 
      # The base implementation raises an error.
170
 
      def each
171
 
        raise NotImplementedError
172
 
      end
173
 
 
174
 
      # Return a convenient sorted list of all generator names.
175
 
      def names
176
 
        map { |spec| spec.name }.sort
177
 
      end
178
 
    end
179
 
 
180
 
 
181
 
    # PathSource looks for generators in a filesystem directory.
182
 
    class PathSource < Source
183
 
      attr_reader :path
184
 
 
185
 
      def initialize(label, path)
186
 
        super label
187
 
        @path = path
188
 
      end
189
 
 
190
 
      # Yield each eligible subdirectory.
191
 
      def each
192
 
        Dir["#{path}/[a-z]*"].each do |dir|
193
 
          if File.directory?(dir)
194
 
            yield Spec.new(File.basename(dir), dir, label)
195
 
          end
196
 
        end
197
 
      end
198
 
    end
199
 
 
200
 
    class AbstractGemSource < Source
201
 
      def initialize
202
 
        super :RubyGems
203
 
      end
204
 
    end
205
 
 
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.
210
 
      def each
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
215
 
          latest
216
 
        }.values.each { |gem|
217
 
          yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
218
 
        }
219
 
      end
220
 
    end
221
 
 
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.
225
 
      def each
226
 
        generator_full_paths.each do |generator|
227
 
          yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label)
228
 
        end
229
 
      end
230
 
 
231
 
      private
232
 
        def generator_full_paths
233
 
          @generator_full_paths ||=
234
 
            Gem::cache.inject({}) do |latest, name_gem|
235
 
              name, gem = name_gem
236
 
              hem = latest[gem.name]
237
 
              latest[gem.name] = gem if hem.nil? or gem.version > hem.version
238
 
              latest
239
 
            end.values.inject([]) do |mem, gem|
240
 
              Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator|
241
 
                mem << generator
242
 
              end
243
 
              mem
244
 
            end
245
 
        end
246
 
    end
247
 
 
248
 
  end
249
 
end