| Class | Rails::Generator::Commands::Create |
| In: |
vendor/rails/railties/lib/rails_generator/commands.rb
|
| Parent: | Base |
Create is the premier generator command. It copies files, creates directories, renders templates, and more.
| SYNONYM_LOOKUP_URI | = | "http://wordnet.princeton.edu/perl/webwn?s=%s" |
Check whether the given class names are already taken by Ruby or Rails. In the future, expand to check other namespaces such as the rest of the user‘s app.
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 157
157: def class_collisions(*class_names)
158:
159: # Initialize some check varibles
160: last_class = Object
161: current_class = nil
162: name = nil
163:
164: class_names.flatten.each do |class_name|
165: # Convert to string to allow symbol arguments.
166: class_name = class_name.to_s
167:
168: # Skip empty strings.
169: class_name.strip.empty? ? next : current_class = class_name
170:
171: # Split the class from its module nesting.
172: nesting = class_name.split('::')
173: name = nesting.pop
174:
175: # Extract the last Module in the nesting.
176: last = nesting.inject(last_class) { |last, nest|
177: break unless last_class.const_defined?(nest)
178: last_class = last_class.const_get(nest)
179: }
180:
181: end
182: # If the last Module exists, check whether the given
183: # class exists and raise a collision if so.
184:
185: if last_class and last_class.const_defined?(name.camelize)
186: raise_class_collision(current_class)
187: end
188: end
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 296
296: def complex_template(relative_source, relative_destination, template_options = {})
297: options = template_options.dup
298: options[:assigns] ||= {}
299: options[:assigns]['template_for_inclusion'] = render_template_part(template_options)
300: template(relative_source, relative_destination, options)
301: end
Create a directory including any missing parent directories. Always skips directories which exist.
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 305
305: def directory(relative_path)
306: path = destination_path(relative_path)
307: if File.exist?(path)
308: logger.exists relative_path
309: else
310: logger.create relative_path
311: unless options[:pretend]
312: FileUtils.mkdir_p(path)
313: # git doesn't require adding the paths, adding the files later will
314: # automatically do a path add.
315:
316: # Subversion doesn't do path adds, so we need to add
317: # each directory individually.
318: # So stack up the directory tree and add the paths to
319: # subversion in order without recursion.
320: if options[:svn]
321: stack = [relative_path]
322: until File.dirname(stack.last) == stack.last # dirname('.') == '.'
323: stack.push File.dirname(stack.last)
324: end
325: stack.reverse_each do |rel_path|
326: svn_path = destination_path(rel_path)
327: system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
328: end
329: end
330: end
331: end
332: end
Copy a file from source to destination with collision checking.
The file_options hash accepts :chmod and :shebang and :collision options. :chmod sets the permissions of the destination file:
file 'config/empty.log', 'log/test.log', :chmod => 0664
:shebang sets the #!/usr/bin/ruby line for scripts
file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby'
:collision sets the collision option only for the destination file:
file 'settings/server.yml', 'config/server.yml', :collision => :skip
Collisions are handled by checking whether the destination file exists and either skipping the file, forcing overwrite, or asking the user what to do.
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 203
203: def file(relative_source, relative_destination, file_options = {}, &block)
204: # Determine full paths for source and destination files.
205: source = source_path(relative_source)
206: destination = destination_path(relative_destination)
207: destination_exists = File.exist?(destination)
208:
209: # If source and destination are identical then we're done.
210: if destination_exists and identical?(source, destination, &block)
211: return logger.identical(relative_destination)
212: end
213:
214: # Check for and resolve file collisions.
215: if destination_exists
216:
217: # Make a choice whether to overwrite the file. :force and
218: # :skip already have their mind made up, but give :ask a shot.
219: choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask
220: when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block)
221: when :force then :force
222: when :skip then :skip
223: else raise "Invalid collision option: #{options[:collision].inspect}"
224: end
225:
226: # Take action based on our choice. Bail out if we chose to
227: # skip the file; otherwise, log our transgression and continue.
228: case choice
229: when :force then logger.force(relative_destination)
230: when :skip then return(logger.skip(relative_destination))
231: else raise "Invalid collision choice: #{choice}.inspect"
232: end
233:
234: # File doesn't exist so log its unbesmirched creation.
235: else
236: logger.create relative_destination
237: end
238:
239: # If we're pretending, back off now.
240: return if options[:pretend]
241:
242: # Write destination file with optional shebang. Yield for content
243: # if block given so templaters may render the source file. If a
244: # shebang is requested, replace the existing shebang or insert a
245: # new one.
246: File.open(destination, 'wb') do |dest|
247: dest.write render_file(source, file_options, &block)
248: end
249:
250: # Optionally change permissions.
251: if file_options[:chmod]
252: FileUtils.chmod(file_options[:chmod], destination)
253: end
254:
255: # Optionally add file to subversion or git
256: system("svn add #{destination}") if options[:svn]
257: system("git add -v #{relative_destination}") if options[:git]
258: end
Checks if the source and the destination file are identical. If passed a block then the source file is a template that needs to first be evaluated before being compared to the destination.
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 263
263: def identical?(source, destination, &block)
264: return false if File.directory? destination
265: source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source)
266: destination = IO.read(destination)
267: source == destination
268: end
When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 343
343: def migration_template(relative_source, relative_destination, template_options = {})
344: migration_directory relative_destination
345: migration_file_name = template_options[:migration_file_name] || file_name
346: raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name)
347: template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options)
348: end
Display a README.
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 335
335: def readme(*relative_sources)
336: relative_sources.flatten.each do |relative_source|
337: logger.readme relative_source
338: puts File.read(source_path(relative_source)) unless options[:pretend]
339: end
340: end
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 350
350: def route_resources(*resources)
351: resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
352: sentinel = 'ActionController::Routing::Routes.draw do |map|'
353:
354: logger.route "map.resources #{resource_list}"
355: unless options[:pretend]
356: gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
357: "#{match}\n map.resources #{resource_list}\n"
358: end
359: end
360: end
Generate a file for a Rails application using an ERuby template. Looks up and evaluates a template by name and writes the result.
The ERB template uses explicit trim mode to best control the proliferation of whitespace in generated code. <%- trims leading whitespace; -%> trims trailing whitespace including one newline.
A hash of template options may be passed as the last argument. The options accepted by the file are accepted as well as :assigns, a hash of variable bindings. Example:
template 'foo', 'bar', :assigns => { :action => 'view' }
Template is implemented in terms of file. It calls file with a block which takes a file handle and returns its rendered contents.
# File vendor/rails/railties/lib/rails_generator/commands.rb, line 284
284: def template(relative_source, relative_destination, template_options = {})
285: file(relative_source, relative_destination, template_options) do |file|
286: # Evaluate any assignments in a temporary, throwaway binding.
287: vars = template_options[:assigns] || {}
288: b = binding
289: vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
290:
291: # Render the source file with the temporary binding.
292: ERB.new(file.read, nil, '-').result(b)
293: end
294: end