Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 374 lines (335 sloc) 13.061 kB
70d2640 @jeremy Cherry-pick core extensions. Don't explicitly require rubygems. Use l…
jeremy authored
1 require 'thor'
f0dd77c Move railties/lib/* into railties/lib/*
Yehuda Katz + Carl Lerche authored
2 require 'rails/generators/actions'
f03890e @josevalim Use Rails generators files organization.
josevalim authored
3
d57b193 @josevalim Add base and port actions. Not functional though.
josevalim authored
4 module Rails
5 module Generators
ea106cf @josevalim Added plugin generators (and a couple of TODOs).
josevalim authored
6 class Error < Thor::Error
7 end
8
d57b193 @josevalim Add base and port actions. Not functional though.
josevalim authored
9 class Base < Thor::Group
8d47078 @josevalim Added source_paths to rails generators. If a template is added to RAI…
josevalim authored
10 include Thor::Actions
11 include Rails::Generators::Actions
12
0efedf2 @josevalim Ensure scaffold works properly even if plural name is given. [#3062]
josevalim authored
13 add_runtime_options!
14
d226f17 @josevalim Ensure that generators can be invoked from any directory.
josevalim authored
15 # Always move to rails source root.
16 #
17 def initialize(*args) #:nodoc:
18 if !invoked?(args) && defined?(Rails.root) && Rails.root
19 self.destination_root = Rails.root
20 FileUtils.cd(destination_root)
21 end
22 super
23 end
24
d57b193 @josevalim Add base and port actions. Not functional though.
josevalim authored
25 # Automatically sets the source root based on the class name.
26 #
27 def self.source_root
8d47078 @josevalim Added source_paths to rails generators. If a template is added to RAI…
josevalim authored
28 @_rails_source_root ||= File.expand_path(File.join(File.dirname(__FILE__),
29 base_name, generator_name, 'templates'))
1845675 @josevalim Added metal generator.
josevalim authored
30 end
31
d5bdf31 @josevalim Refactoring out descriptions.
josevalim authored
32 # Tries to get the description from a USAGE file one folder above the source
33 # root otherwise uses a default description.
34 #
35 def self.desc(description=nil)
36 return super if description
c03585a @josevalim Show invoked generators options on meta generators.
josevalim authored
37 usage = File.expand_path(File.join(source_root, "..", "USAGE"))
d5bdf31 @josevalim Refactoring out descriptions.
josevalim authored
38
39 @desc ||= if File.exist?(usage)
40 File.read(usage)
41 else
42 "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
43 end
44 end
45
a748bb7 @josevalim Get base_name from class_name.
josevalim authored
46 # Convenience method to get the namespace from the class name. It's the
47 # same as Thor default except that the Generator at the end of the class
48 # is removed.
1845675 @josevalim Added metal generator.
josevalim authored
49 #
d5bdf31 @josevalim Refactoring out descriptions.
josevalim authored
50 def self.namespace(name=nil)
51 return super if name
ff48e23 @josevalim Make Observer generator agnostic.
josevalim authored
52 @namespace ||= super.sub(/_generator$/, '')
d57b193 @josevalim Add base and port actions. Not functional though.
josevalim authored
53 end
b9d8faf @josevalim Update templates to be backwards compatible.
josevalim authored
54
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
55 # Invoke a generator based on the value supplied by the user to the
56 # given option named "name". A class option is created when this method
37ba004 @josevalim Removed invoke_if and allow hook_for to accept boolean options.
josevalim authored
57 # is invoked and you can set a hash to customize it.
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
58 #
59 # ==== Examples
60 #
0efedf2 @josevalim Ensure scaffold works properly even if plural name is given. [#3062]
josevalim authored
61 # module Rails::Generators
62 # class ControllerGenerator < Base
63 # hook_for :test_framework, :aliases => "-t"
64 # end
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
65 # end
66 #
67 # The example above will create a test framework option and will invoke
68 # a generator based on the user supplied value.
69 #
70 # For example, if the user invoke the controller generator as:
71 #
72 # ruby script/generate controller Account --test-framework=test_unit
73 #
acaeaf3 @josevalim Add a fixture replacement hook to TestUnit model generator.
josevalim authored
74 # The controller generator will then try to invoke the following generators:
75 #
76 # "rails:generators:test_unit", "test_unit:generators:controller", "test_unit"
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
77 #
acaeaf3 @josevalim Add a fixture replacement hook to TestUnit model generator.
josevalim authored
78 # In this case, the "test_unit:generators:controller" is available and is
79 # invoked. This allows any test framework to hook into Rails as long as it
80 # provides any of the hooks above.
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
81 #
0efedf2 @josevalim Ensure scaffold works properly even if plural name is given. [#3062]
josevalim authored
82 # ==== Options
83 #
84 # This lookup can be customized with two options: :base and :as. The first
85 # is the root module value and in the example above defaults to "rails".
86 # The later defaults to the generator name, without the "Generator" ending.
87 #
88 # Let's suppose you are creating a generator that needs to invoke the
89 # controller generator from test unit. Your first attempt is:
90 #
91 # class AwesomeGenerator < Rails::Generators::Base
92 # hook_for :test_framework
93 # end
94 #
95 # The lookup in this case for test_unit as input is:
96 #
97 # "test_unit:generators:awesome", "test_unit"
98 #
99 # Which is not the desired the lookup. You can change it by providing the
100 # :as option:
101 #
102 # class AwesomeGenerator < Rails::Generators::Base
103 # hook_for :test_framework, :as => :controller
104 # end
105 #
106 # And now it will lookup at:
107 #
108 # "test_unit:generators:awesome", "test_unit"
109 #
110 # Similarly, if you want it to also lookup in the rails namespace, you just
111 # need to provide the :base value:
112 #
113 # class AwesomeGenerator < Rails::Generators::Base
114 # hook_for :test_framework, :base => :rails, :as => :controller
115 # end
116 #
117 # And the lookup is exactly the same as previously:
118 #
119 # "rails:generators:test_unit", "test_unit:generators:controller", "test_unit"
120 #
121 # ==== Switches
122 #
123 # All hooks come with switches for user interface. If the user don't want
124 # to use any test framework, he can do:
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
125 #
126 # ruby script/generate controller Account --skip-test-framework
127 #
128 # Or similarly:
129 #
130 # ruby script/generate controller Account --no-test-framework
131 #
37ba004 @josevalim Removed invoke_if and allow hook_for to accept boolean options.
josevalim authored
132 # ==== Boolean hooks
133 #
134 # In some cases, you want to provide a boolean hook. For example, webrat
135 # developers might want to have webrat available on controller generator.
136 # This can be achieved as:
137 #
138 # Rails::Generators::ControllerGenerator.hook_for :webrat, :type => :boolean
139 #
140 # Then, if you want, webrat to be invoked, just supply:
141 #
142 # ruby script/generate controller Account --webrat
143 #
144 # The hooks lookup is similar as above:
145 #
146 # "rails:generators:webrat", "webrat:generators:controller", "webrat"
147 #
95ef9bd @josevalim Simplifying resource generator.
josevalim authored
148 # ==== Custom invocations
149 #
37ba004 @josevalim Removed invoke_if and allow hook_for to accept boolean options.
josevalim authored
150 # You can also supply a block to hook_for to customize how the hook is
95ef9bd @josevalim Simplifying resource generator.
josevalim authored
151 # going to be invoked. The block receives two parameters, an instance
152 # of the current class and the klass to be invoked.
153 #
154 # For example, in the resource generator, the controller should be invoked
155 # with a pluralized class name. By default, it is invoked with the same
156 # name as the resource generator, which is singular. To change this, we
157 # can give a block to customize how the controller can be invoked.
158 #
159 # hook_for :resource_controller do |instance, controller|
160 # instance.invoke controller, [ instance.name.pluralize ]
161 # end
162 #
163 def self.hook_for(*names, &block)
9068bc0 @josevalim Added ResourceGenerator.
josevalim authored
164 options = names.extract_options!
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
165 in_base = options.delete(:in) || base_name
166 as_hook = options.delete(:as) || generator_name
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
167
168 names.each do |name|
37ba004 @josevalim Removed invoke_if and allow hook_for to accept boolean options.
josevalim authored
169 defaults = if options[:type] == :boolean
170 { }
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
171 elsif [true, false].include?(default_value_for_option(name, options))
37ba004 @josevalim Removed invoke_if and allow hook_for to accept boolean options.
josevalim authored
172 { :banner => "" }
173 else
174 { :desc => "#{name.to_s.humanize} to be invoked", :banner => "NAME" }
175 end
d5d0b8e @josevalim Removing unecessary class methods.
josevalim authored
176
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
177 unless class_options.key?(name)
178 class_option name, defaults.merge!(options)
179 end
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
180
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
181 hooks[name] = [ in_base, as_hook ]
182 invoke_from_option name, options, &block
95a5cc6 @josevalim Add invoke_iff and invoke_for to the public interface.
josevalim authored
183 end
184 end
185
dec1ee7 @josevalim Started with scaffold controller and added remove_hook_for.
josevalim authored
186 # Remove a previously added hook.
187 #
188 # ==== Examples
189 #
190 # remove_hook_for :orm
191 #
192 def self.remove_hook_for(*names)
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
193 remove_invocation *names
194
dec1ee7 @josevalim Started with scaffold controller and added remove_hook_for.
josevalim authored
195 names.each do |name|
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
196 hooks.delete(name)
dec1ee7 @josevalim Started with scaffold controller and added remove_hook_for.
josevalim authored
197 end
198 end
199
c9ea217 @josevalim Generators are configured on initialization if RAILS_ENV=generators.
josevalim authored
200 # Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
d5d0b8e @josevalim Removing unecessary class methods.
josevalim authored
201 #
d5225c1 @josevalim Do not require options on class_option.
josevalim authored
202 def self.class_option(name, options={}) #:nodoc:
d5d0b8e @josevalim Removing unecessary class methods.
josevalim authored
203 options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
204 options[:aliases] = default_aliases_for_option(name, options)
205 options[:default] = default_value_for_option(name, options)
d5d0b8e @josevalim Removing unecessary class methods.
josevalim authored
206 super(name, options)
207 end
208
8d47078 @josevalim Added source_paths to rails generators. If a template is added to RAI…
josevalim authored
209 # Cache source root and add lib/generators/base/generator/templates to
210 # source paths.
211 #
212 def self.inherited(base) #:nodoc:
213 super
214
216e8c6 @josevalim Update Rails Generators to use Thor 0.12.0.
josevalim authored
215 # Cache source root, we need to do this, since __FILE__ is a relative value
216 # and can point to wrong directions when inside an specified directory.
217 base.source_root
218
cb9a175 @jeremy Fix generators tests that expect a class name
jeremy authored
219 if base.name && base.name !~ /Base$/ && defined?(Rails.root) && Rails.root
2110a52 Deprecate RAILS_ROOT in favor of Rails.root (which proxies to the app…
Carl Lerche authored
220 path = File.expand_path(File.join(Rails.root, 'lib', 'templates'))
67c2f53 @josevalim Allow templates for non namespaced generators too.
josevalim authored
221 if base.name.include?('::')
222 base.source_paths << File.join(path, base.base_name, base.generator_name)
223 else
224 base.source_paths << File.join(path, base.generator_name)
225 end
8d47078 @josevalim Added source_paths to rails generators. If a template is added to RAI…
josevalim authored
226 end
227 end
228
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
229 protected
04eb5b6 @josevalim Use Rails default banner.
josevalim authored
230
2f3681d @josevalim Clean up class collisions check and a class method helper.
josevalim authored
231 # Check whether the given class names are already taken by user
232 # application or Ruby on Rails.
ed33c29 @josevalim Added class collision checks.
josevalim authored
233 #
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
234 def class_collisions(*class_names) #:nodoc:
ed33c29 @josevalim Added class collision checks.
josevalim authored
235 return unless behavior == :invoke
236
237 class_names.flatten.each do |class_name|
238 class_name = class_name.to_s
239 next if class_name.strip.empty?
240
241 # Split the class from its module nesting
242 nesting = class_name.split('::')
243 last_name = nesting.pop
244
245 # Hack to limit const_defined? to non-inherited on 1.9
246 extra = []
247 extra << false unless Object.method(:const_defined?).arity == 1
248
249 # Extract the last Module in the nesting
250 last = nesting.inject(Object) do |last, nest|
251 break unless last.const_defined?(nest, *extra)
252 last.const_get(nest)
253 end
254
255 if last && last.const_defined?(last_name.camelize, *extra)
256 raise Error, "The name '#{class_name}' is either already used in your application " <<
257 "or reserved by Ruby on Rails. Please choose an alternative and run " <<
258 "this generator again."
259 end
260 end
261 end
262
d226f17 @josevalim Ensure that generators can be invoked from any directory.
josevalim authored
263 # Check if this generator was invoked from another one by inspecting
264 # parameters.
265 #
266 def invoked?(args)
267 args.last.is_a?(Hash) && args.last.key?(:invocations)
268 end
269
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
270 # Use Rails default banner.
271 #
272 def self.banner
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
273 "#{$0} #{generator_name} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]"
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
274 end
1845675 @josevalim Added metal generator.
josevalim authored
275
a748bb7 @josevalim Get base_name from class_name.
josevalim authored
276 # Sets the base_name taking into account the current class namespace.
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
277 #
d5bdf31 @josevalim Refactoring out descriptions.
josevalim authored
278 def self.base_name
cb9a175 @jeremy Fix generators tests that expect a class name
jeremy authored
279 if name
280 @base_name ||= name.split('::').first.underscore
281 end
1845675 @josevalim Added metal generator.
josevalim authored
282 end
04eb5b6 @josevalim Use Rails default banner.
josevalim authored
283
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
284 # Removes the namespaces and get the generator name. For example,
285 # Rails::Generators::MetalGenerator will return "metal" as generator name.
286 #
287 def self.generator_name
cb9a175 @jeremy Fix generators tests that expect a class name
jeremy authored
288 if name
289 @generator_name ||= begin
290 klass_name = name.to_s.split('::').last
291 klass_name.sub!(/Generator$/, '')
292 klass_name.underscore
293 end
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
294 end
295 end
7a8e284 @josevalim Refactor shebang to a class method, so other generators can use it.
josevalim authored
296
7022b58 @josevalim Allow namespaced configuration on generators.
josevalim authored
297 # Return the default value for the option name given doing a lookup in
298 # Rails::Generators.options.
299 #
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
300 def self.default_value_for_option(name, options)
301 config = Rails::Generators.options
baa4781 @josevalim Allow nil and false to be given as configuration values and avoid cre…
josevalim authored
302 generator, base = generator_name.to_sym, base_name.to_sym
7022b58 @josevalim Allow namespaced configuration on generators.
josevalim authored
303
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
304 if config[generator] && config[generator].key?(name)
305 config[generator][name]
306 elsif config[base] && config[base].key?(name)
307 config[base][name]
308 elsif config[:rails].key?(name)
309 config[:rails][name]
7022b58 @josevalim Allow namespaced configuration on generators.
josevalim authored
310 else
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
311 options[:default]
7022b58 @josevalim Allow namespaced configuration on generators.
josevalim authored
312 end
313 end
314
315 # Return default aliases for the option name given doing a lookup in
316 # Rails::Generators.aliases.
317 #
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
318 def self.default_aliases_for_option(name, options)
319 config = Rails::Generators.aliases
baa4781 @josevalim Allow nil and false to be given as configuration values and avoid cre…
josevalim authored
320 generator, base = generator_name.to_sym, base_name.to_sym
7022b58 @josevalim Allow namespaced configuration on generators.
josevalim authored
321
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
322 if config[generator] && config[generator].key?(name)
323 config[generator][name]
324 elsif config[base] && config[base].key?(name)
325 config[base][name]
326 elsif config[:rails].key?(name)
327 config[:rails][name]
7022b58 @josevalim Allow namespaced configuration on generators.
josevalim authored
328 else
419ca7a @josevalim Ensure that developers options are overwritten by user options.
josevalim authored
329 options[:aliases]
7022b58 @josevalim Allow namespaced configuration on generators.
josevalim authored
330 end
331 end
332
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
333 # Keep hooks configuration that are used on prepare_for_invocation.
c03585a @josevalim Show invoked generators options on meta generators.
josevalim authored
334 #
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
335 def self.hooks #:nodoc:
336 @hooks ||= from_superclass(:hooks, {})
00b168e @josevalim Show second level invocations on usage.
josevalim authored
337 end
338
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
339 # Prepare class invocation to search on Rails namespace if a previous
340 # added hook is being used.
00b168e @josevalim Show second level invocations on usage.
josevalim authored
341 #
a06c825 @josevalim Updated vendored Thor to 0.11.1 and update Rails::Generators.
josevalim authored
342 def self.prepare_for_invocation(name, value) #:nodoc:
343 if value && constants = self.hooks[name]
344 Rails::Generators.find_by_namespace(value, *constants)
345 else
346 super
00b168e @josevalim Show second level invocations on usage.
josevalim authored
347 end
c03585a @josevalim Show invoked generators options on meta generators.
josevalim authored
348 end
349
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
350 # Small macro to add ruby as an option to the generator with proper
351 # default value plus an instance helper method called shebang.
352 #
353 def self.add_shebang_option!
8ff214e @josevalim Use the proper shebang when using Ruby 1.9 or jruby or windows.
josevalim authored
354 class_option :ruby, :type => :string, :aliases => "-r", :default => Thor::Util.ruby_command,
a748bb7 @josevalim Get base_name from class_name.
josevalim authored
355 :desc => "Path to the Ruby binary of your choice", :banner => "PATH"
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
356
8ff214e @josevalim Use the proper shebang when using Ruby 1.9 or jruby or windows.
josevalim authored
357 no_tasks {
358 define_method :shebang do
359 @shebang ||= begin
360 command = if options[:ruby] == Thor::Util.ruby_command
361 "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}"
362 else
363 options[:ruby]
364 end
365 "#!#{command}"
366 end
a7ba4b9 @josevalim TestUnit, you have a home.
josevalim authored
367 end
8ff214e @josevalim Use the proper shebang when using Ruby 1.9 or jruby or windows.
josevalim authored
368 }
7a8e284 @josevalim Refactor shebang to a class method, so other generators can use it.
josevalim authored
369 end
370
371 end
d57b193 @josevalim Add base and port actions. Not functional though.
josevalim authored
372 end
373 end
Something went wrong with that request. Please try again.