Create is the premier generator command. It copies files, creates directories, renders templates, and more.
- class_collisions
- complex_template
- directory
- file
- identical?
- migration_template
- readme
- route_resources
- template
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.
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 173 173: def class_collisions(*class_names) 174: path = class_names.shift 175: class_names.flatten.each do |class_name| 176: # Convert to string to allow symbol arguments. 177: class_name = class_name.to_s 178: 179: # Skip empty strings. 180: next if class_name.strip.empty? 181: 182: # Split the class from its module nesting. 183: nesting = class_name.split('::') 184: name = nesting.pop 185: 186: # Hack to limit const_defined? to non-inherited on 1.9. 187: extra = [] 188: extra << false unless Object.method(:const_defined?).arity == 1 189: 190: # Extract the last Module in the nesting. 191: last = nesting.inject(Object) { |last, nest| 192: break unless last.const_defined?(nest, *extra) 193: last.const_get(nest) 194: } 195: 196: # If the last Module exists, check whether the given 197: # class exists and raise a collision if so. 198: if last and last.const_defined?(name.camelize, *extra) 199: raise_class_collision(class_name) 200: end 201: end 202: end
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 310 310: def complex_template(relative_source, relative_destination, template_options = {}) 311: options = template_options.dup 312: options[:assigns] ||= {} 313: options[:assigns]['template_for_inclusion'] = render_template_part(template_options) 314: template(relative_source, relative_destination, options) 315: end
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 319 319: def directory(relative_path) 320: path = destination_path(relative_path) 321: if File.exist?(path) 322: logger.exists relative_path 323: else 324: logger.create relative_path 325: unless options[:pretend] 326: FileUtils.mkdir_p(path) 327: # git doesn't require adding the paths, adding the files later will 328: # automatically do a path add. 329: 330: # Subversion doesn't do path adds, so we need to add 331: # each directory individually. 332: # So stack up the directory tree and add the paths to 333: # subversion in order without recursion. 334: if options[:svn] 335: stack = [relative_path] 336: until File.dirname(stack.last) == stack.last # dirname('.') == '.' 337: stack.push File.dirname(stack.last) 338: end 339: stack.reverse_each do |rel_path| 340: svn_path = destination_path(rel_path) 341: system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn')) 342: end 343: end 344: end 345: end 346: 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.
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 217 217: def file(relative_source, relative_destination, file_options = {}, &block) 218: # Determine full paths for source and destination files. 219: source = source_path(relative_source) 220: destination = destination_path(relative_destination) 221: destination_exists = File.exist?(destination) 222: 223: # If source and destination are identical then we're done. 224: if destination_exists and identical?(source, destination, &block) 225: return logger.identical(relative_destination) 226: end 227: 228: # Check for and resolve file collisions. 229: if destination_exists 230: 231: # Make a choice whether to overwrite the file. :force and 232: # :skip already have their mind made up, but give :ask a shot. 233: choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask 234: when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block) 235: when :force then :force 236: when :skip then :skip 237: else raise "Invalid collision option: #{options[:collision].inspect}" 238: end 239: 240: # Take action based on our choice. Bail out if we chose to 241: # skip the file; otherwise, log our transgression and continue. 242: case choice 243: when :force then logger.force(relative_destination) 244: when :skip then return(logger.skip(relative_destination)) 245: else raise "Invalid collision choice: #{choice}.inspect" 246: end 247: 248: # File doesn't exist so log its unbesmirched creation. 249: else 250: logger.create relative_destination 251: end 252: 253: # If we're pretending, back off now. 254: return if options[:pretend] 255: 256: # Write destination file with optional shebang. Yield for content 257: # if block given so templaters may render the source file. If a 258: # shebang is requested, replace the existing shebang or insert a 259: # new one. 260: File.open(destination, 'wb') do |dest| 261: dest.write render_file(source, file_options, &block) 262: end 263: 264: # Optionally change permissions. 265: if file_options[:chmod] 266: FileUtils.chmod(file_options[:chmod], destination) 267: end 268: 269: # Optionally add file to subversion or git 270: system("svn add #{destination}") if options[:svn] 271: system("git add -v #{relative_destination}") if options[:git] 272: 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.
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 277 277: def identical?(source, destination, &block) 278: return false if File.directory? destination 279: source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source) 280: destination = IO.read(destination) 281: source == destination 282: end
When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 357 357: def migration_template(relative_source, relative_destination, template_options = {}) 358: migration_directory relative_destination 359: migration_file_name = template_options[:migration_file_name] || file_name 360: raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name) 361: template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options) 362: end
Display a README.
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 349 349: def readme(*relative_sources) 350: relative_sources.flatten.each do |relative_source| 351: logger.readme relative_source 352: puts File.read(source_path(relative_source)) unless options[:pretend] 353: end 354: end
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 364 364: def route_resources(*resources) 365: resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') 366: sentinel = 'ActionController::Routing::Routes.draw do |map|' 367: 368: logger.route "map.resources #{resource_list}" 369: unless options[:pretend] 370: gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| 371: "#{match}\n map.resources #{resource_list}\n" 372: end 373: end 374: 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.
[ show source ]
# File railties/lib/rails_generator/commands.rb, line 298 298: def template(relative_source, relative_destination, template_options = {}) 299: file(relative_source, relative_destination, template_options) do |file| 300: # Evaluate any assignments in a temporary, throwaway binding. 301: vars = template_options[:assigns] || {} 302: b = template_options[:binding] || binding 303: vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b } 304: 305: # Render the source file with the temporary binding. 306: ERB.new(file.read, nil, '-').result(b) 307: end 308: end