Removed superfluous 'tracks' directory at the root of the repository.

Testing commits to github.
This commit is contained in:
bsag 2008-05-20 21:28:26 +01:00
parent 6a42901514
commit 4cbf5a34d3
2269 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,263 @@
require File.dirname(__FILE__) + '/options'
require File.dirname(__FILE__) + '/manifest'
require File.dirname(__FILE__) + '/spec'
require File.dirname(__FILE__) + '/generated_attribute'
module Rails
# Rails::Generator is a code generation platform tailored for the Rails
# web application framework. Generators are easily invoked within Rails
# applications to add and remove components such as models and controllers.
# New generators are easy to create and may be distributed as RubyGems,
# tarballs, or Rails plugins for inclusion system-wide, per-user,
# or per-application.
#
# For actual examples see the rails_generator/generators directory in the
# Rails source (or the +railties+ directory if you have frozen the Rails
# source in your application).
#
# Generators may subclass other generators to provide variations that
# require little or no new logic but replace the template files.
#
# For a RubyGem, put your generator class and templates in the +lib+
# directory. For a Rails plugin, make a +generators+ directory at the
# root of your plugin.
#
# The layout of generator files can be seen in the built-in
# +controller+ generator:
#
# generators/
# components/
# controller/
# controller_generator.rb
# templates/
# controller.rb
# functional_test.rb
# helper.rb
# view.html.erb
#
# The directory name (+controller+) matches the name of the generator file
# (controller_generator.rb) and class (+ControllerGenerator+). The files
# that will be copied or used as templates are stored in the +templates+
# directory.
#
# The filenames of the templates don't matter, but choose something that
# will be self-explanatory since you will be referencing these in the
# +manifest+ method inside your generator subclass.
#
#
module Generator
class GeneratorError < StandardError; end
class UsageError < GeneratorError; end
# The base code generator is bare-bones. It sets up the source and
# destination paths and tells the logger whether to keep its trap shut.
#
# It's useful for copying files such as stylesheets, images, or
# javascripts.
#
# For more comprehensive template-based passive code generation with
# arguments, you'll want Rails::Generator::NamedBase.
#
# Generators create a manifest of the actions they perform then hand
# the manifest to a command which replays the actions to do the heavy
# lifting (such as checking for existing files or creating directories
# if needed). Create, destroy, and list commands are included. Since a
# single manifest may be used by any command, creating new generators is
# as simple as writing some code templates and declaring what you'd like
# to do with them.
#
# The manifest method must be implemented by subclasses, returning a
# Rails::Generator::Manifest. The +record+ method is provided as a
# convenience for manifest creation. Example:
#
# class StylesheetGenerator < Rails::Generator::Base
# def manifest
# record do |m|
# m.directory('public/stylesheets')
# m.file('application.css', 'public/stylesheets/application.css')
# end
# end
# end
#
# See Rails::Generator::Commands::Create for a list of methods available
# to the manifest.
class Base
include Options
# Declare default options for the generator. These options
# are inherited to subclasses.
default_options :collision => :ask, :quiet => false
# A logger instance available everywhere in the generator.
cattr_accessor :logger
# Every generator that is dynamically looked up is tagged with a
# Spec describing where it was found.
class_inheritable_accessor :spec
attr_reader :source_root, :destination_root, :args
def initialize(runtime_args, runtime_options = {})
@args = runtime_args
parse!(@args, runtime_options)
# Derive source and destination paths.
@source_root = options[:source] || File.join(spec.path, 'templates')
if options[:destination]
@destination_root = options[:destination]
elsif defined? ::RAILS_ROOT
@destination_root = ::RAILS_ROOT
end
# Silence the logger if requested.
logger.quiet = options[:quiet]
# Raise usage error if help is requested.
usage if options[:help]
end
# Generators must provide a manifest. Use the +record+ method to create
# a new manifest and record your generator's actions.
def manifest
raise NotImplementedError, "No manifest for '#{spec.name}' generator."
end
# Return the full path from the source root for the given path.
# Example for source_root = '/source':
# source_path('some/path.rb') == '/source/some/path.rb'
#
# The given path may include a colon ':' character to indicate that
# the file belongs to another generator. This notation allows any
# generator to borrow files from another. Example:
# source_path('model:fixture.yml') = '/model/source/path/fixture.yml'
def source_path(relative_source)
# Check whether we're referring to another generator's file.
name, path = relative_source.split(':', 2)
# If not, return the full path to our source file.
if path.nil?
File.join(source_root, name)
# Otherwise, ask our referral for the file.
else
# FIXME: this is broken, though almost always true. Others'
# source_root are not necessarily the templates dir.
File.join(self.class.lookup(name).path, 'templates', path)
end
end
# Return the full path from the destination root for the given path.
# Example for destination_root = '/dest':
# destination_path('some/path.rb') == '/dest/some/path.rb'
def destination_path(relative_destination)
File.join(destination_root, relative_destination)
end
protected
# Convenience method for generator subclasses to record a manifest.
def record
Rails::Generator::Manifest.new(self) { |m| yield m }
end
# Override with your own usage banner.
def banner
"Usage: #{$0} #{spec.name} [options]"
end
# Read USAGE from file in generator base path.
def usage_message
File.read(File.join(spec.path, 'USAGE')) rescue ''
end
end
# The base generator for named components: models, controllers, mailers,
# etc. The target name is taken as the first argument and inflected to
# singular, plural, class, file, and table forms for your convenience.
# The remaining arguments are aliased to +actions+ as an array for
# controller and mailer convenience.
#
# Several useful local variables and methods are populated in the
# +initialize+ method. See below for a list of Attributes and
# External Aliases available to both the manifest and to all templates.
#
# If no name is provided, the generator raises a usage error with content
# optionally read from the USAGE file in the generator's base path.
#
# For example, the +controller+ generator takes the first argument as
# the name of the class and subsequent arguments as the names of
# actions to be generated:
#
# ./script/generate controller Article index new create
#
# See Rails::Generator::Base for a discussion of manifests,
# Rails::Generator::Commands::Create for methods available to the manifest,
# and Rails::Generator for a general discussion of generators.
class NamedBase < Base
attr_reader :name, :class_name, :singular_name, :plural_name, :table_name
attr_reader :class_path, :file_path, :class_nesting, :class_nesting_depth
alias_method :file_name, :singular_name
alias_method :actions, :args
def initialize(runtime_args, runtime_options = {})
super
# Name argument is required.
usage if runtime_args.empty?
@args = runtime_args.dup
base_name = @args.shift
assign_names!(base_name)
end
protected
# Override with your own usage banner.
def banner
"Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]"
end
def attributes
@attributes ||= @args.collect do |attribute|
Rails::Generator::GeneratedAttribute.new(*attribute.split(":"))
end
end
private
def assign_names!(name)
@name = name
base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name)
@class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name)
@table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name
@table_name.gsub! '/', '_'
if @class_nesting.empty?
@class_name = @class_name_without_nesting
else
@table_name = @class_nesting.underscore << "_" << @table_name
@class_name = "#{@class_nesting}::#{@class_name_without_nesting}"
end
end
# Extract modules from filesystem-style or ruby-style path:
# good/fun/stuff
# Good::Fun::Stuff
# produce the same results.
def extract_modules(name)
modules = name.include?('/') ? name.split('/') : name.split('::')
name = modules.pop
path = modules.map { |m| m.underscore }
file_path = (path + [name.underscore]).join('/')
nesting = modules.map { |m| m.camelize }.join('::')
[name, path, file_path, nesting, modules.size]
end
def inflect_names(name)
camel = name.camelize
under = camel.underscore
plural = under.pluralize
[camel, under, plural]
end
end
end
end

View file

@ -0,0 +1,591 @@
require 'delegate'
require 'optparse'
require 'fileutils'
require 'tempfile'
require 'erb'
module Rails
module Generator
module Commands
# Here's a convenient way to get a handle on generator commands.
# Command.instance('destroy', my_generator) instantiates a Destroy
# delegate of my_generator ready to do your dirty work.
def self.instance(command, generator)
const_get(command.to_s.camelize).new(generator)
end
# Even more convenient access to commands. Include Commands in
# the generator Base class to get a nice #command instance method
# which returns a delegate for the requested command.
def self.included(base)
base.send(:define_method, :command) do |command|
Commands.instance(command, self)
end
end
# Generator commands delegate Rails::Generator::Base and implement
# a standard set of actions. Their behavior is defined by the way
# they respond to these actions: Create brings life; Destroy brings
# death; List passively observes.
#
# Commands are invoked by replaying (or rewinding) the generator's
# manifest of actions. See Rails::Generator::Manifest and
# Rails::Generator::Base#manifest method that generator subclasses
# are required to override.
#
# Commands allows generators to "plug in" invocation behavior, which
# corresponds to the GoF Strategy pattern.
class Base < DelegateClass(Rails::Generator::Base)
# Replay action manifest. RewindBase subclass rewinds manifest.
def invoke!
manifest.replay(self)
end
def dependency(generator_name, args, runtime_options = {})
logger.dependency(generator_name) do
self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke!
end
end
# Does nothing for all commands except Create.
def class_collisions(*class_names)
end
# Does nothing for all commands except Create.
def readme(*args)
end
protected
def migration_directory(relative_path)
directory(@migration_directory = relative_path)
end
def existing_migrations(file_name)
Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/)
end
def migration_exists?(file_name)
not existing_migrations(file_name).empty?
end
def current_migration_number
Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path|
n = File.basename(file_path).split('_', 2).first.to_i
if n > max then n else max end
end
end
def next_migration_number
current_migration_number + 1
end
def next_migration_string(padding = 3)
"%.#{padding}d" % next_migration_number
end
def gsub_file(relative_destination, regexp, *args, &block)
path = destination_path(relative_destination)
content = File.read(path).gsub(regexp, *args, &block)
File.open(path, 'wb') { |file| file.write(content) }
end
private
# Ask the user interactively whether to force collision.
def force_file_collision?(destination, src, dst, file_options = {}, &block)
$stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] "
case $stdin.gets.chomp
when /\Ad\z/i
Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp|
temp.write render_file(src, file_options, &block)
temp.rewind
$stdout.puts `#{diff_cmd} #{dst} #{temp.path}`
end
puts "retrying"
raise 'retry diff'
when /\Aa\z/i
$stdout.puts "forcing #{spec.name}"
options[:collision] = :force
when /\Aq\z/i
$stdout.puts "aborting #{spec.name}"
raise SystemExit
when /\An\z/i then :skip
when /\Ay\z/i then :force
else
$stdout.puts <<-HELP
Y - yes, overwrite
n - no, do not overwrite
a - all, overwrite this and all others
q - quit, abort
d - diff, show the differences between the old and the new
h - help, show this help
HELP
raise 'retry'
end
rescue
retry
end
def diff_cmd
ENV['RAILS_DIFF'] || 'diff -u'
end
def render_template_part(template_options)
# Getting Sandbox to evaluate part template in it
part_binding = template_options[:sandbox].call.sandbox_binding
part_rel_path = template_options[:insert]
part_path = source_path(part_rel_path)
# Render inner template within Sandbox binding
rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding)
begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id])
end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id])
begin_mark + rendered_part + end_mark
end
def template_part_mark(name, id)
"<!--[#{name}:#{id}]-->\n"
end
end
# Base class for commands which handle generator actions in reverse, such as Destroy.
class RewindBase < Base
# Rewind action manifest.
def invoke!
manifest.rewind(self)
end
end
# Create is the premier generator command. It copies files, creates
# directories, renders templates, and more.
class Create < Base
# 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.
def class_collisions(*class_names)
class_names.flatten.each do |class_name|
# Convert to string to allow symbol arguments.
class_name = class_name.to_s
# Skip empty strings.
next if class_name.strip.empty?
# Split the class from its module nesting.
nesting = class_name.split('::')
name = nesting.pop
# Extract the last Module in the nesting.
last = nesting.inject(Object) { |last, nest|
break unless last.const_defined?(nest)
last.const_get(nest)
}
# If the last Module exists, check whether the given
# class exists and raise a collision if so.
if last and last.const_defined?(name.camelize)
raise_class_collision(class_name)
end
end
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.
def file(relative_source, relative_destination, file_options = {}, &block)
# Determine full paths for source and destination files.
source = source_path(relative_source)
destination = destination_path(relative_destination)
destination_exists = File.exist?(destination)
# If source and destination are identical then we're done.
if destination_exists and identical?(source, destination, &block)
return logger.identical(relative_destination)
end
# Check for and resolve file collisions.
if destination_exists
# Make a choice whether to overwrite the file. :force and
# :skip already have their mind made up, but give :ask a shot.
choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask
when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block)
when :force then :force
when :skip then :skip
else raise "Invalid collision option: #{options[:collision].inspect}"
end
# Take action based on our choice. Bail out if we chose to
# skip the file; otherwise, log our transgression and continue.
case choice
when :force then logger.force(relative_destination)
when :skip then return(logger.skip(relative_destination))
else raise "Invalid collision choice: #{choice}.inspect"
end
# File doesn't exist so log its unbesmirched creation.
else
logger.create relative_destination
end
# If we're pretending, back off now.
return if options[:pretend]
# Write destination file with optional shebang. Yield for content
# if block given so templaters may render the source file. If a
# shebang is requested, replace the existing shebang or insert a
# new one.
File.open(destination, 'wb') do |dest|
dest.write render_file(source, file_options, &block)
end
# Optionally change permissions.
if file_options[:chmod]
FileUtils.chmod(file_options[:chmod], destination)
end
# Optionally add file to subversion
system("svn add #{destination}") if options[:svn]
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.
def identical?(source, destination, &block)
return false if File.directory? destination
source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source)
destination = IO.read(destination)
source == destination
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.
def template(relative_source, relative_destination, template_options = {})
file(relative_source, relative_destination, template_options) do |file|
# Evaluate any assignments in a temporary, throwaway binding.
vars = template_options[:assigns] || {}
b = binding
vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
# Render the source file with the temporary binding.
ERB.new(file.read, nil, '-').result(b)
end
end
def complex_template(relative_source, relative_destination, template_options = {})
options = template_options.dup
options[:assigns] ||= {}
options[:assigns]['template_for_inclusion'] = render_template_part(template_options)
template(relative_source, relative_destination, options)
end
# Create a directory including any missing parent directories.
# Always directories which exist.
def directory(relative_path)
path = destination_path(relative_path)
if File.exist?(path)
logger.exists relative_path
else
logger.create relative_path
unless options[:pretend]
FileUtils.mkdir_p(path)
# Subversion doesn't do path adds, so we need to add
# each directory individually.
# So stack up the directory tree and add the paths to
# subversion in order without recursion.
if options[:svn]
stack=[relative_path]
until File.dirname(stack.last) == stack.last # dirname('.') == '.'
stack.push File.dirname(stack.last)
end
stack.reverse_each do |rel_path|
svn_path = destination_path(rel_path)
system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
end
end
end
end
end
# Display a README.
def readme(*relative_sources)
relative_sources.flatten.each do |relative_source|
logger.readme relative_source
puts File.read(source_path(relative_source)) unless options[:pretend]
end
end
# When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
def migration_template(relative_source, relative_destination, template_options = {})
migration_directory relative_destination
migration_file_name = template_options[:migration_file_name] || file_name
raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name)
template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options)
end
def route_resources(*resources)
resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
sentinel = 'ActionController::Routing::Routes.draw do |map|'
logger.route "map.resources #{resource_list}"
unless options[:pretend]
gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
"#{match}\n map.resources #{resource_list}\n"
end
end
end
private
def render_file(path, options = {})
File.open(path, 'rb') do |file|
if block_given?
yield file
else
content = ''
if shebang = options[:shebang]
content << "#!#{shebang}\n"
if line = file.gets
content << "line\n" if line !~ /^#!/
end
end
content << file.read
end
end
end
# Raise a usage error with an informative WordNet suggestion.
# Thanks to Florian Gross (flgr).
def raise_class_collision(class_name)
message = <<end_message
The name '#{class_name}' is reserved by Ruby on Rails.
Please choose an alternative and run this generator again.
end_message
if suggest = find_synonyms(class_name)
message << "\n Suggestions: \n\n"
message << suggest.join("\n")
end
raise UsageError, message
end
SYNONYM_LOOKUP_URI = "http://wordnet.princeton.edu/cgi-bin/webwn2.0?stage=2&word=%s&posnumber=1&searchtypenumber=2&senses=&showglosses=1"
# Look up synonyms on WordNet. Thanks to Florian Gross (flgr).
def find_synonyms(word)
require 'open-uri'
require 'timeout'
timeout(5) do
open(SYNONYM_LOOKUP_URI % word) do |stream|
data = stream.read.gsub("&nbsp;", " ").gsub("<BR>", "")
data.scan(/^Sense \d+\n.+?\n\n/m)
end
end
rescue Exception
return nil
end
end
# Undo the actions performed by a generator. Rewind the action
# manifest and attempt to completely erase the results of each action.
class Destroy < RewindBase
# Remove a file if it exists and is a file.
def file(relative_source, relative_destination, file_options = {})
destination = destination_path(relative_destination)
if File.exist?(destination)
logger.rm relative_destination
unless options[:pretend]
if options[:svn]
# If the file has been marked to be added
# but has not yet been checked in, revert and delete
if options[:svn][relative_destination]
system("svn revert #{destination}")
FileUtils.rm(destination)
else
# If the directory is not in the status list, it
# has no modifications so we can simply remove it
system("svn rm #{destination}")
end
else
FileUtils.rm(destination)
end
end
else
logger.missing relative_destination
return
end
end
# Templates are deleted just like files and the actions take the
# same parameters, so simply alias the file method.
alias_method :template, :file
# Remove each directory in the given path from right to left.
# Remove each subdirectory if it exists and is a directory.
def directory(relative_path)
parts = relative_path.split('/')
until parts.empty?
partial = File.join(parts)
path = destination_path(partial)
if File.exist?(path)
if Dir[File.join(path, '*')].empty?
logger.rmdir partial
unless options[:pretend]
if options[:svn]
# If the directory has been marked to be added
# but has not yet been checked in, revert and delete
if options[:svn][relative_path]
system("svn revert #{path}")
FileUtils.rmdir(path)
else
# If the directory is not in the status list, it
# has no modifications so we can simply remove it
system("svn rm #{path}")
end
else
FileUtils.rmdir(path)
end
end
else
logger.notempty partial
end
else
logger.missing partial
end
parts.pop
end
end
def complex_template(*args)
# nothing should be done here
end
# When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}".
def migration_template(relative_source, relative_destination, template_options = {})
migration_directory relative_destination
migration_file_name = template_options[:migration_file_name] || file_name
unless migration_exists?(migration_file_name)
puts "There is no migration named #{migration_file_name}"
return
end
existing_migrations(migration_file_name).each do |file_path|
file(relative_source, file_path, template_options)
end
end
def route_resources(*resources)
resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
look_for = "\n map.resources #{resource_list}\n"
logger.route "map.resources #{resource_list}"
gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
end
end
# List a generator's action manifest.
class List < Base
def dependency(generator_name, args, options = {})
logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})"
end
def class_collisions(*class_names)
logger.class_collisions class_names.join(', ')
end
def file(relative_source, relative_destination, options = {})
logger.file relative_destination
end
def template(relative_source, relative_destination, options = {})
logger.template relative_destination
end
def complex_template(relative_source, relative_destination, options = {})
logger.template "#{options[:insert]} inside #{relative_destination}"
end
def directory(relative_path)
logger.directory "#{destination_path(relative_path)}/"
end
def readme(*args)
logger.readme args.join(', ')
end
def migration_template(relative_source, relative_destination, options = {})
migration_directory relative_destination
logger.migration_template file_name
end
def route_resources(*resources)
resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
logger.route "map.resources #{resource_list}"
end
end
# Update generator's action manifest.
class Update < Create
def file(relative_source, relative_destination, options = {})
# logger.file relative_destination
end
def template(relative_source, relative_destination, options = {})
# logger.template relative_destination
end
def complex_template(relative_source, relative_destination, template_options = {})
begin
dest_file = destination_path(relative_destination)
source_to_update = File.readlines(dest_file).join
rescue Errno::ENOENT
logger.missing relative_destination
return
end
logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}"
begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id]))
end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id]))
# Refreshing inner part of the template with freshly rendered part.
rendered_part = render_template_part(template_options)
source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part)
File.open(dest_file, 'w') { |file| file.write(source_to_update) }
end
def directory(relative_path)
# logger.directory "#{destination_path(relative_path)}/"
end
end
end
end
end

View file

@ -0,0 +1,42 @@
require 'optparse'
module Rails
module Generator
class GeneratedAttribute
attr_accessor :name, :type, :column
def initialize(name, type)
@name, @type = name, type.to_sym
@column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, @type)
end
def field_type
@field_type ||= case type
when :integer, :float, :decimal then :text_field
when :datetime, :timestamp, :time then :datetime_select
when :date then :date_select
when :string then :text_field
when :text then :text_area
when :boolean then :check_box
else
:text_field
end
end
def default
@default ||= case type
when :integer then 1
when :float then 1.5
when :decimal then "9.99"
when :datetime, :timestamp, :time then Time.now.to_s(:db)
when :date then Date.today.to_s(:db)
when :string then "MyString"
when :text then "MyText"
when :boolean then false
else
""
end
end
end
end
end

View file

@ -0,0 +1,9 @@
Description:
The 'rails' command creates a new Rails application with a default
directory structure and configuration at the path you specify.
Example:
rails ~/Code/Ruby/weblog
This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
See the README in the newly created application to get going.

View file

@ -0,0 +1,179 @@
require 'rbconfig'
require 'digest/md5'
require 'rails_generator/secret_key_generator'
class AppGenerator < Rails::Generator::Base
DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
Config::CONFIG['ruby_install_name'])
DATABASES = %w(mysql oracle postgresql sqlite2 sqlite3 frontbase)
default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || "sqlite3"),
:shebang => DEFAULT_SHEBANG, :freeze => false
mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.."
def initialize(runtime_args, runtime_options = {})
super
usage if args.empty?
usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db]))
@destination_root = args.shift
@app_name = File.basename(File.expand_path(@destination_root))
end
def manifest
# Use /usr/bin/env if no special shebang was specified
script_options = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] }
dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] }
# duplicate CGI::Session#generate_unique_id
md5 = Digest::MD5.new
now = Time.now
md5 << now.to_s
md5 << String(now.usec)
md5 << String(rand(0))
md5 << String($$)
md5 << @app_name
# Do our best to generate a secure secret key for CookieStore
secret = Rails::SecretKeyGenerator.new(@app_name).generate_secret
record do |m|
# Root directory and all subdirectories.
m.directory ''
BASEDIRS.each { |path| m.directory path }
# Root
m.file "fresh_rakefile", "Rakefile"
m.file "README", "README"
# Application
m.template "helpers/application.rb", "app/controllers/application.rb", :assigns => { :app_name => @app_name, :app_secret => md5.hexdigest }
m.template "helpers/application_helper.rb", "app/helpers/application_helper.rb"
m.template "helpers/test_helper.rb", "test/test_helper.rb"
# database.yml and .htaccess
m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => {
:app_name => @app_name,
:socket => options[:db] == "mysql" ? mysql_socket_location : nil
}
m.template "configs/routes.rb", "config/routes.rb"
m.template "configs/apache.conf", "public/.htaccess"
# Initializers
m.template "configs/initializers/inflections.rb", "config/initializers/inflections.rb"
m.template "configs/initializers/mime_types.rb", "config/initializers/mime_types.rb"
# Environments
m.file "environments/boot.rb", "config/boot.rb"
m.template "environments/environment.rb", "config/environment.rb", :assigns => { :freeze => options[:freeze], :app_name => @app_name, :app_secret => secret }
m.file "environments/production.rb", "config/environments/production.rb"
m.file "environments/development.rb", "config/environments/development.rb"
m.file "environments/test.rb", "config/environments/test.rb"
# Scripts
%w( about console destroy generate performance/benchmarker performance/profiler performance/request process/reaper process/spawner process/inspector runner server plugin ).each do |file|
m.file "bin/#{file}", "script/#{file}", script_options
end
# Dispatches
m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options
m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options
m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options
# HTML files
%w(404 422 500 index).each do |file|
m.template "html/#{file}.html", "public/#{file}.html"
end
m.template "html/favicon.ico", "public/favicon.ico"
m.template "html/robots.txt", "public/robots.txt"
m.file "html/images/rails.png", "public/images/rails.png"
# Javascripts
m.file "html/javascripts/prototype.js", "public/javascripts/prototype.js"
m.file "html/javascripts/effects.js", "public/javascripts/effects.js"
m.file "html/javascripts/dragdrop.js", "public/javascripts/dragdrop.js"
m.file "html/javascripts/controls.js", "public/javascripts/controls.js"
m.file "html/javascripts/application.js", "public/javascripts/application.js"
# Docs
m.file "doc/README_FOR_APP", "doc/README_FOR_APP"
# Logs
%w(server production development test).each { |file|
m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666
}
end
end
protected
def banner
"Usage: #{$0} /path/to/your/app [options]"
end
def add_options!(opt)
opt.separator ''
opt.separator 'Options:'
opt.on("-r", "--ruby=path", String,
"Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
"Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
opt.on("-d", "--database=name", String,
"Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3).",
"Default: mysql") { |v| options[:db] = v }
opt.on("-f", "--freeze",
"Freeze Rails in vendor/rails from the gems generating the skeleton",
"Default: false") { |v| options[:freeze] = v }
end
def mysql_socket_location
MYSQL_SOCKET_LOCATIONS.find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
end
# Installation skeleton. Intermediate directories are automatically
# created so don't sweat their absence here.
BASEDIRS = %w(
app/controllers
app/helpers
app/models
app/views/layouts
config/environments
config/initializers
db
doc
lib
lib/tasks
log
public/images
public/javascripts
public/stylesheets
script/performance
script/process
test/fixtures
test/functional
test/integration
test/mocks/development
test/mocks/test
test/unit
vendor
vendor/plugins
tmp/sessions
tmp/sockets
tmp/cache
tmp/pids
)
MYSQL_SOCKET_LOCATIONS = [
"/tmp/mysql.sock", # default
"/var/run/mysqld/mysqld.sock", # debian/gentoo
"/var/tmp/mysql.sock", # freebsd
"/var/lib/mysql/mysql.sock", # fedora
"/opt/local/lib/mysql/mysql.sock", # fedora
"/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
"/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
"/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
"/opt/lampp/var/mysql/mysql.sock" # xampp for linux
]
end

View file

@ -0,0 +1,29 @@
Description:
Stubs out a new controller and its views. Pass the controller name, either
CamelCased or under_scored, and a list of views as arguments.
To create a controller within a module, specify the controller name as a
path like 'parent_module/controller_name'.
This generates a controller class in app/controllers, view templates in
app/views/controller_name, a helper class in app/helpers, and a functional
test suite in test/functional.
Example:
`./script/generate controller CreditCard open debit credit close`
Credit card controller with URLs like /credit_card/debit.
Controller: app/controllers/credit_card_controller.rb
Views: app/views/credit_card/debit.html.erb [...]
Helper: app/helpers/credit_card_helper.rb
Test: test/functional/credit_card_controller_test.rb
Modules Example:
`./script/generate controller 'admin/credit_card' suspend late_fee`
Credit card admin controller with URLs /admin/credit_card/suspend.
Controller: app/controllers/admin/credit_card_controller.rb
Views: app/views/admin/credit_card/debit.html.erb [...]
Helper: app/helpers/admin/credit_card_helper.rb
Test: test/functional/admin/credit_card_controller_test.rb

View file

@ -0,0 +1,37 @@
class ControllerGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions class_path, "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper"
# Controller, helper, views, and test directories.
m.directory File.join('app/controllers', class_path)
m.directory File.join('app/helpers', class_path)
m.directory File.join('app/views', class_path, file_name)
m.directory File.join('test/functional', class_path)
# Controller class, functional test, and helper class.
m.template 'controller.rb',
File.join('app/controllers',
class_path,
"#{file_name}_controller.rb")
m.template 'functional_test.rb',
File.join('test/functional',
class_path,
"#{file_name}_controller_test.rb")
m.template 'helper.rb',
File.join('app/helpers',
class_path,
"#{file_name}_helper.rb")
# View template for each action.
actions.each do |action|
path = File.join('app/views', class_path, file_name, "#{action}.html.erb")
m.template 'view.html.erb', path,
:assigns => { :action => action, :path => path }
end
end
end
end

View file

@ -0,0 +1,10 @@
class <%= class_name %>Controller < ApplicationController
<% if options[:scaffold] -%>
scaffold :<%= singular_name %>
<% end -%>
<% for action in actions -%>
def <%= action %>
end
<% end -%>
end

View file

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
class <%= class_name %>ControllerTest < ActionController::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end

View file

@ -0,0 +1,2 @@
module <%= class_name %>Helper
end

View file

@ -0,0 +1,2 @@
<h1><%= class_name %>#<%= action %></h1>
<p>Find me in <%= path %></p>

View file

@ -0,0 +1,8 @@
Description:
Stubs out a new integration test. Pass the name of the test, either
CamelCased or under_scored, as an argument. The new test class is
generated in test/integration/testname_test.rb
Example:
`./script/generate integration_test GeneralStories` creates a GeneralStories
integration test in test/integration/general_stories_test.rb

View file

@ -0,0 +1,16 @@
class IntegrationTestGenerator < Rails::Generator::NamedBase
default_options :skip_migration => false
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions class_path, class_name, "#{class_name}Test"
# integration test directory
m.directory File.join('test/integration', class_path)
# integration test stub
m.template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb")
end
end
end

View file

@ -0,0 +1,10 @@
require "#{File.dirname(__FILE__)}<%= '/..' * class_nesting_depth %>/../test_helper"
class <%= class_name %>Test < ActionController::IntegrationTest
# fixtures :your, :models
# Replace this with your real tests.
def test_truth
assert true
end
end

View file

@ -0,0 +1,16 @@
Description:
Stubs out a new mailer and its views. Pass the mailer name, either
CamelCased or under_scored, and an optional list of emails as arguments.
This generates a mailer class in app/models, view templates in
app/views/mailer_name, a unit test in test/unit, and fixtures in
test/fixtures.
Example:
`./script/generate mailer Notifications signup forgot_password invoice`
creates a Notifications mailer class, views, test, and fixtures:
Mailer: app/models/notifications.rb
Views: app/views/notifications/signup.erb [...]
Test: test/unit/test/unit/notifications_test.rb
Fixtures: test/fixtures/notifications/signup [...]

View file

@ -0,0 +1,34 @@
class MailerGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions class_path, class_name, "#{class_name}Test"
# Mailer, view, test, and fixture directories.
m.directory File.join('app/models', class_path)
m.directory File.join('app/views', file_path)
m.directory File.join('test/unit', class_path)
m.directory File.join('test/fixtures', file_path)
# Mailer class and unit test.
m.template "mailer.rb", File.join('app/models',
class_path,
"#{file_name}.rb")
m.template "unit_test.rb", File.join('test/unit',
class_path,
"#{file_name}_test.rb")
# View template and fixture for each action.
actions.each do |action|
relative_path = File.join(file_path, action)
view_path = File.join('app/views', "#{relative_path}.erb")
fixture_path = File.join('test/fixtures', relative_path)
m.template "view.erb", view_path,
:assigns => { :action => action, :path => view_path }
m.template "fixture.erb", fixture_path,
:assigns => { :action => action, :path => view_path }
end
end
end
end

View file

@ -0,0 +1,3 @@
<%= class_name %>#<%= action %>
Find me in <%= path %>

View file

@ -0,0 +1,13 @@
class <%= class_name %> < ActionMailer::Base
<% for action in actions -%>
def <%= action %>(sent_at = Time.now)
@subject = '<%= class_name %>#<%= action %>'
@body = {}
@recipients = ''
@from = ''
@sent_on = sent_at
@headers = {}
end
<% end -%>
end

View file

@ -0,0 +1,21 @@
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
class <%= class_name %>Test < ActionMailer::TestCase
tests <%= class_name %>
<% for action in actions -%>
def test_<%= action %>
@expected.subject = '<%= class_name %>#<%= action %>'
@expected.body = read_fixture('<%= action %>')
@expected.date = Time.now
assert_equal @expected.encoded, <%= class_name %>.create_<%= action %>(@expected.date).encoded
end
<% end -%>
<% if actions.blank? -%>
# replace this with your real tests
def test_truth
assert true
end
<% end -%>
end

View file

@ -0,0 +1,3 @@
<%= class_name %>#<%= action %>
Find me in <%= path %>

View file

@ -0,0 +1,29 @@
Description:
Stubs out a new database migration. Pass the migration name, either
CamelCased or under_scored, and an optional list of attribute pairs as arguments.
A migration class is generated in db/migrate prefixed by the latest migration number.
You can name your migration in either of these formats to generate add/remove
column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable
Example:
`./script/generate migration AddSslFlag`
With 4 existing migrations, this creates the AddSslFlag migration in
db/migrate/005_add_ssl_flag.rb
`./script/generate migration AddTitleBodyToPost title:string body:text published:boolean`
This will create the AddTitleBodyToPost in db/migrate/005_add_title_body_to_post.rb with
this in the Up migration:
add_column :posts, :title, :string
add_column :posts, :body, :text
add_column :posts, :published, :boolean
And this in the Down migration:
remove_column :posts, :published
remove_column :posts, :body
remove_column :posts, :title

View file

@ -0,0 +1,20 @@
class MigrationGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
m.migration_template 'migration.rb', 'db/migrate', :assigns => get_local_assigns
end
end
private
def get_local_assigns
returning(assigns = {}) do
if class_name.underscore =~ /^(add|remove)_.*_(?:to|from)_(.*)/
assigns[:migration_action] = $1
assigns[:table_name] = $2.pluralize
else
assigns[:attributes] = []
end
end
end
end

View file

@ -0,0 +1,11 @@
class <%= class_name.underscore.camelize %> < ActiveRecord::Migration
def self.up<% attributes.each do |attribute| %>
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%>
<%- end %>
end
def self.down<% attributes.reverse.each do |attribute| %>
<%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%>
<%- end %>
end
end

View file

@ -0,0 +1,27 @@
Description:
Stubs out a new model. Pass the model name, either CamelCased or
under_scored, and an optional list of attribute pairs as arguments.
Attribute pairs are column_name:sql_type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
specify them by hand as 'created_at:datetime updated_at:datetime'.
You don't have to think up every attribute up front, but it helps to
sketch out a few so you can start working with the model immediately.
This generates a model class in app/models, a unit test in test/unit,
a test fixture in test/fixtures/singular_name.yml, and a migration in
db/migrate.
Examples:
`./script/generate model account`
creates an Account model, test, fixture, and migration:
Model: app/models/account.rb
Test: test/unit/account_test.rb
Fixtures: test/fixtures/accounts.yml
Migration: db/migrate/XXX_add_accounts.rb
`./script/generate model post title:string body:text published:boolean`
creates a Post model with a string title, text body, and published flag.

View file

@ -0,0 +1,45 @@
class ModelGenerator < Rails::Generator::NamedBase
default_options :skip_timestamps => false, :skip_migration => false, :skip_fixture => false
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions class_path, class_name, "#{class_name}Test"
# Model, test, and fixture directories.
m.directory File.join('app/models', class_path)
m.directory File.join('test/unit', class_path)
m.directory File.join('test/fixtures', class_path)
# Model class, unit test, and fixtures.
m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
unless options[:skip_fixture]
m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml")
end
unless options[:skip_migration]
m.migration_template 'migration.rb', 'db/migrate', :assigns => {
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
}, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
end
end
end
protected
def banner
"Usage: #{$0} #{spec.name} ModelName [field:type, field:type]"
end
def add_options!(opt)
opt.separator ''
opt.separator 'Options:'
opt.on("--skip-timestamps",
"Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
opt.on("--skip-migration",
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
opt.on("--skip-fixture",
"Don't generation a fixture file for this model") { |v| options[:skip_fixture] = v}
end
end

View file

@ -0,0 +1,19 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
<% unless attributes.empty? -%>
one:
<% for attribute in attributes -%>
<%= attribute.name %>: <%= attribute.default %>
<% end -%>
two:
<% for attribute in attributes -%>
<%= attribute.name %>: <%= attribute.default %>
<% end -%>
<% else -%>
# one:
# column: value
#
# two:
# column: value
<% end -%>

View file

@ -0,0 +1,16 @@
class <%= migration_name %> < ActiveRecord::Migration
def self.up
create_table :<%= table_name %> do |t|
<% for attribute in attributes -%>
t.<%= attribute.type %> :<%= attribute.name %>
<% end -%>
<% unless options[:skip_timestamps] %>
t.timestamps
<% end -%>
end
end
def self.down
drop_table :<%= table_name %>
end
end

View file

@ -0,0 +1,2 @@
class <%= class_name %> < ActiveRecord::Base
end

View file

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
class <%= class_name %>Test < ActiveSupport::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end

View file

@ -0,0 +1,13 @@
Description:
Stubs out a new observer. Pass the observer name, either CamelCased or
under_scored, as an argument.
The generator creates an observer class in app/models and a unit test in
test/unit.
Example:
`./script/generate observer Account`
creates an Account observer and unit test:
Observer: app/models/account_observer.rb
Test: test/unit/account_observer_test.rb

View file

@ -0,0 +1,16 @@
class ObserverGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions class_path, "#{class_name}Observer", "#{class_name}ObserverTest"
# Observer, and test directories.
m.directory File.join('app/models', class_path)
m.directory File.join('test/unit', class_path)
# Observer class and unit test fixtures.
m.template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb")
m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb")
end
end
end

View file

@ -0,0 +1,2 @@
class <%= class_name %>Observer < ActiveRecord::Observer
end

View file

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
class <%= class_name %>ObserverTest < Test::Unit::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end

View file

@ -0,0 +1,25 @@
Description:
Stubs out a new plugin. Pass the plugin name, either CamelCased or
under_scored, as an argument. Pass --with-generator to add an example
generator also.
This creates a plugin in vendor/plugins including an init.rb and README
as well as standard lib, task, and test directories.
Example:
`./script/generate plugin BrowserFilters`
creates a standard browser_filters plugin:
vendor/plugins/browser_filters/README
vendor/plugins/browser_filters/init.rb
vendor/plugins/browser_filters/install.rb
vendor/plugins/browser_filters/lib/browser_filters.rb
vendor/plugins/browser_filters/test/browser_filters_test.rb
vendor/plugins/browser_filters/tasks/browser_filters_tasks.rake
./script/generate plugin BrowserFilters --with-generator
creates a browser_filters generator also:
vendor/plugins/browser_filters/generators/browser_filters/browser_filters_generator.rb
vendor/plugins/browser_filters/generators/browser_filters/USAGE
vendor/plugins/browser_filters/generators/browser_filters/templates/

View file

@ -0,0 +1,39 @@
class PluginGenerator < Rails::Generator::NamedBase
attr_reader :plugin_path
def initialize(runtime_args, runtime_options = {})
@with_generator = runtime_args.delete("--with-generator")
super
@plugin_path = "vendor/plugins/#{file_name}"
end
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions class_path, class_name
m.directory "#{plugin_path}/lib"
m.directory "#{plugin_path}/tasks"
m.directory "#{plugin_path}/test"
m.template 'README', "#{plugin_path}/README"
m.template 'MIT-LICENSE', "#{plugin_path}/MIT-LICENSE"
m.template 'Rakefile', "#{plugin_path}/Rakefile"
m.template 'init.rb', "#{plugin_path}/init.rb"
m.template 'install.rb', "#{plugin_path}/install.rb"
m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb"
m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb"
m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake"
m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb"
if @with_generator
m.directory "#{plugin_path}/generators"
m.directory "#{plugin_path}/generators/#{file_name}"
m.directory "#{plugin_path}/generators/#{file_name}/templates"
m.template 'generator.rb', "#{plugin_path}/generators/#{file_name}/#{file_name}_generator.rb"
m.template 'USAGE', "#{plugin_path}/generators/#{file_name}/USAGE"
end
end
end
end

View file

@ -0,0 +1,20 @@
Copyright (c) <%= Date.today.year %> [name of plugin creator]
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,13 @@
<%= class_name %>
<%= "=" * class_name.size %>
Introduction goes here.
Example
=======
Example goes here.
Copyright (c) <%= Date.today.year %> [name of plugin creator], released under the MIT license

View file

@ -0,0 +1,22 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the <%= file_name %> plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the <%= file_name %> plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = '<%= class_name %>'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end

View file

@ -0,0 +1,8 @@
Description:
Explain the generator
Example:
./script/generate <%= file_name %> Thing
This will create:
what/will/it/create

View file

@ -0,0 +1,8 @@
class <%= class_name %>Generator < Rails::Generator::NamedBase
def manifest
record do |m|
# m.directory "lib"
# m.template 'README', "README"
end
end
end

View file

@ -0,0 +1 @@
# Include hook code here

View file

@ -0,0 +1 @@
# Install hook code here

View file

@ -0,0 +1 @@
# <%= class_name %>

View file

@ -0,0 +1,4 @@
# desc "Explaining what the task does"
# task :<%= file_name %> do
# # Task goes here
# end

View file

@ -0,0 +1 @@
# Uninstall hook code here

View file

@ -0,0 +1,8 @@
require 'test/unit'
class <%= class_name %>Test < Test::Unit::TestCase
# Replace this with your real tests.
def test_this_plugin
flunk
end
end

View file

@ -0,0 +1,23 @@
Description:
Stubs out a new resource including an empty model and controller suitable
for a restful, resource-oriented application. Pass the singular model name,
either CamelCased or under_scored, as the first argument, and an optional
list of attribute pairs.
Attribute pairs are column_name:sql_type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
specify them by hand as 'created_at:datetime updated_at:datetime'.
You don't have to think up every attribute up front, but it helps to
sketch out a few so you can start working with the resource immediately.
This creates a model, controller, tests and fixtures for both, and the
corresponding map.resources declaration in config/routes.rb
Unlike the scaffold generator, the resource generator does not create
views or add any methods to the generated controller.
Examples:
`./script/generate resource post` # no attributes
`./script/generate resource post title:string body:text published:boolean`
`./script/generate resource purchase order_id:integer amount:decimal`

View file

@ -0,0 +1,74 @@
class ResourceGenerator < Rails::Generator::NamedBase
default_options :skip_timestamps => false, :skip_migration => false
attr_reader :controller_name,
:controller_class_path,
:controller_file_path,
:controller_class_nesting,
:controller_class_nesting_depth,
:controller_class_name,
:controller_singular_name,
:controller_plural_name
alias_method :controller_file_name, :controller_singular_name
alias_method :controller_table_name, :controller_plural_name
def initialize(runtime_args, runtime_options = {})
super
@controller_name = @name.pluralize
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
@controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
if @controller_class_nesting.empty?
@controller_class_name = @controller_class_name_without_nesting
else
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
end
end
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
m.class_collisions(class_path, "#{class_name}")
# Controller, helper, views, and test directories.
m.directory(File.join('app/models', class_path))
m.directory(File.join('app/controllers', controller_class_path))
m.directory(File.join('app/helpers', controller_class_path))
m.directory(File.join('app/views', controller_class_path, controller_file_name))
m.directory(File.join('test/functional', controller_class_path))
m.directory(File.join('test/unit', class_path))
m.dependency 'model', [name] + @args, :collision => :skip
m.template(
'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
)
m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
m.route_resources controller_file_name
end
end
protected
def banner
"Usage: #{$0} resource ModelName [field:type, field:type]"
end
def add_options!(opt)
opt.separator ''
opt.separator 'Options:'
opt.on("--skip-timestamps",
"Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
opt.on("--skip-migration",
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
end
def model_name
class_name.demodulize
end
end

View file

@ -0,0 +1,2 @@
class <%= controller_class_name %>Controller < ApplicationController
end

View file

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
class <%= controller_class_name %>ControllerTest < ActionController::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end

View file

@ -0,0 +1,2 @@
module <%= controller_class_name %>Helper
end

View file

@ -0,0 +1,25 @@
Description:
Scaffolds an entire resource, from model and migration to controller and
views, along with a full test suite. The resource is ready to use as a
starting point for your restful, resource-oriented application.
Pass the name of the model, either CamelCased or under_scored, as the first
argument, and an optional list of attribute pairs.
Attribute pairs are column_name:sql_type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
specify them by hand as 'created_at:datetime updated_at:datetime'.
You don't have to think up every attribute up front, but it helps to
sketch out a few so you can start working with the resource immediately.
For example, `scaffold post title:string body:text published:boolean`
gives you a model with those three attributes, a controller that handles
the create/show/update/destroy, forms to create and edit your posts, and
an index that lists them all, as well as a map.resources :posts
declaration in config/routes.rb.
Examples:
`./script/generate scaffold post` # no attributes, view will be anemic
`./script/generate scaffold post title:string body:text published:boolean`
`./script/generate scaffold purchase order_id:integer amount:decimal`

View file

@ -0,0 +1,92 @@
class ScaffoldGenerator < Rails::Generator::NamedBase
default_options :skip_timestamps => false, :skip_migration => false
attr_reader :controller_name,
:controller_class_path,
:controller_file_path,
:controller_class_nesting,
:controller_class_nesting_depth,
:controller_class_name,
:controller_underscore_name,
:controller_singular_name,
:controller_plural_name
alias_method :controller_file_name, :controller_underscore_name
alias_method :controller_table_name, :controller_plural_name
def initialize(runtime_args, runtime_options = {})
super
@controller_name = @name.pluralize
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
@controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
@controller_singular_name=base_name.singularize
if @controller_class_nesting.empty?
@controller_class_name = @controller_class_name_without_nesting
else
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
end
end
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
m.class_collisions(class_path, "#{class_name}")
# Controller, helper, views, and test directories.
m.directory(File.join('app/models', class_path))
m.directory(File.join('app/controllers', controller_class_path))
m.directory(File.join('app/helpers', controller_class_path))
m.directory(File.join('app/views', controller_class_path, controller_file_name))
m.directory(File.join('app/views/layouts', controller_class_path))
m.directory(File.join('test/functional', controller_class_path))
m.directory(File.join('test/unit', class_path))
for action in scaffold_views
m.template(
"view_#{action}.html.erb",
File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb")
)
end
# Layout and stylesheet.
m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb"))
m.template('style.css', 'public/stylesheets/scaffold.css')
m.dependency 'model', [name] + @args, :collision => :skip
m.template(
'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
)
m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
m.route_resources controller_file_name
end
end
protected
# Override with your own usage banner.
def banner
"Usage: #{$0} scaffold ModelName [field:type, field:type]"
end
def add_options!(opt)
opt.separator ''
opt.separator 'Options:'
opt.on("--skip-timestamps",
"Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
opt.on("--skip-migration",
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
end
def scaffold_views
%w[ index show new edit ]
end
def model_name
class_name.demodulize
end
end

View file

@ -0,0 +1,85 @@
class <%= controller_class_name %>Controller < ApplicationController
# GET /<%= table_name %>
# GET /<%= table_name %>.xml
def index
@<%= table_name %> = <%= class_name %>.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @<%= table_name %> }
end
end
# GET /<%= table_name %>/1
# GET /<%= table_name %>/1.xml
def show
@<%= file_name %> = <%= class_name %>.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @<%= file_name %> }
end
end
# GET /<%= table_name %>/new
# GET /<%= table_name %>/new.xml
def new
@<%= file_name %> = <%= class_name %>.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @<%= file_name %> }
end
end
# GET /<%= table_name %>/1/edit
def edit
@<%= file_name %> = <%= class_name %>.find(params[:id])
end
# POST /<%= table_name %>
# POST /<%= table_name %>.xml
def create
@<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
respond_to do |format|
if @<%= file_name %>.save
flash[:notice] = '<%= class_name %> was successfully created.'
format.html { redirect_to(@<%= file_name %>) }
format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
else
format.html { render :action => "new" }
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
end
end
end
# PUT /<%= table_name %>/1
# PUT /<%= table_name %>/1.xml
def update
@<%= file_name %> = <%= class_name %>.find(params[:id])
respond_to do |format|
if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
flash[:notice] = '<%= class_name %> was successfully updated.'
format.html { redirect_to(@<%= file_name %>) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /<%= table_name %>/1
# DELETE /<%= table_name %>/1.xml
def destroy
@<%= file_name %> = <%= class_name %>.find(params[:id])
@<%= file_name %>.destroy
respond_to do |format|
format.html { redirect_to(<%= table_name %>_url) }
format.xml { head :ok }
end
end
end

View file

@ -0,0 +1,45 @@
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
class <%= controller_class_name %>ControllerTest < ActionController::TestCase
def test_should_get_index
get :index
assert_response :success
assert_not_nil assigns(:<%= table_name %>)
end
def test_should_get_new
get :new
assert_response :success
end
def test_should_create_<%= file_name %>
assert_difference('<%= class_name %>.count') do
post :create, :<%= file_name %> => { }
end
assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
end
def test_should_show_<%= file_name %>
get :show, :id => <%= table_name %>(:one).id
assert_response :success
end
def test_should_get_edit
get :edit, :id => <%= table_name %>(:one).id
assert_response :success
end
def test_should_update_<%= file_name %>
put :update, :id => <%= table_name %>(:one).id, :<%= file_name %> => { }
assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
end
def test_should_destroy_<%= file_name %>
assert_difference('<%= class_name %>.count', -1) do
delete :destroy, :id => <%= table_name %>(:one).id
end
assert_redirected_to <%= table_name %>_path
end
end

View file

@ -0,0 +1,2 @@
module <%= controller_class_name %>Helper
end

View file

@ -0,0 +1,17 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title><%= controller_class_name %>: <%%= controller.action_name %></title>
<%%= stylesheet_link_tag 'scaffold' %>
</head>
<body>
<p style="color: green"><%%= flash[:notice] %></p>
<%%= yield %>
</body>
</html>

View file

@ -0,0 +1,74 @@
body { background-color: #fff; color: #333; }
body, p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
line-height: 18px;
}
pre {
background-color: #eee;
padding: 10px;
font-size: 11px;
}
a { color: #000; }
a:visited { color: #666; }
a:hover { color: #fff; background-color:#000; }
.fieldWithErrors {
padding: 2px;
background-color: red;
display: table;
}
#errorExplanation {
width: 400px;
border: 2px solid red;
padding: 7px;
padding-bottom: 12px;
margin-bottom: 20px;
background-color: #f0f0f0;
}
#errorExplanation h2 {
text-align: left;
font-weight: bold;
padding: 5px 5px 5px 15px;
font-size: 12px;
margin: -7px;
background-color: #c00;
color: #fff;
}
#errorExplanation p {
color: #333;
margin-bottom: 0;
padding: 5px;
}
#errorExplanation ul li {
font-size: 12px;
list-style: square;
}
div.uploadStatus {
margin: 5px;
}
div.progressBar {
margin: 5px;
}
div.progressBar div.border {
background-color: #fff;
border: 1px solid gray;
width: 100%;
}
div.progressBar div.background {
background-color: #333;
height: 18px;
width: 0%;
}

View file

@ -0,0 +1,19 @@
<h1>Editing <%= singular_name %></h1>
<%%= error_messages_for :<%= singular_name %> %>
<%% form_for(@<%= singular_name %>) do |f| %>
<% for attribute in attributes -%>
<p>
<b><%= attribute.column.human_name %></b><br />
<%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
</p>
<% end -%>
<p>
<%%= f.submit "Update" %>
</p>
<%% end %>
<%%= link_to 'Show', @<%= singular_name %> %> |
<%%= link_to 'Back', <%= plural_name %>_path %>

View file

@ -0,0 +1,24 @@
<h1>Listing <%= plural_name %></h1>
<table>
<tr>
<% for attribute in attributes -%>
<th><%= attribute.column.human_name %></th>
<% end -%>
</tr>
<%% for <%= singular_name %> in @<%= plural_name %> %>
<tr>
<% for attribute in attributes -%>
<td><%%=h <%= singular_name %>.<%= attribute.name %> %></td>
<% end -%>
<td><%%= link_to 'Show', <%= singular_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_name %>_path(<%= singular_name %>) %></td>
<td><%%= link_to 'Destroy', <%= singular_name %>, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<%% end %>
</table>
<br />
<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %>

View file

@ -0,0 +1,18 @@
<h1>New <%= singular_name %></h1>
<%%= error_messages_for :<%= singular_name %> %>
<%% form_for(@<%= singular_name %>) do |f| %>
<% for attribute in attributes -%>
<p>
<b><%= attribute.column.human_name %></b><br />
<%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
</p>
<% end -%>
<p>
<%%= f.submit "Create" %>
</p>
<%% end %>
<%%= link_to 'Back', <%= plural_name %>_path %>

View file

@ -0,0 +1,10 @@
<% for attribute in attributes -%>
<p>
<b><%= attribute.column.human_name %>:</b>
<%%=h @<%= singular_name %>.<%= attribute.name %> %>
</p>
<% end -%>
<%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> |
<%%= link_to 'Back', <%= plural_name %>_path %>

View file

@ -0,0 +1,10 @@
Description:
Creates a migration to add the sessions table used by the Active Record
session store. Pass the migration name, either CamelCased or under_scored,
as an argument.
Example:
`./script/generate session_migration CreateSessionTable`
With 4 existing migrations, this creates the AddSessionTable migration
in db/migrate/005_add_session_table.rb

View file

@ -0,0 +1,18 @@
class SessionMigrationGenerator < Rails::Generator::NamedBase
def initialize(runtime_args, runtime_options = {})
runtime_args << 'add_session_table' if runtime_args.empty?
super
end
def manifest
record do |m|
m.migration_template 'migration.rb', 'db/migrate',
:assigns => { :session_table_name => default_session_table_name }
end
end
protected
def default_session_table_name
ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session'
end
end

View file

@ -0,0 +1,16 @@
class <%= class_name %> < ActiveRecord::Migration
def self.up
create_table :<%= session_table_name %> do |t|
t.string :session_id, :null => false
t.text :data
t.timestamps
end
add_index :<%= session_table_name %>, :session_id
add_index :<%= session_table_name %>, :updated_at
end
def self.down
drop_table :<%= session_table_name %>
end
end

View file

@ -0,0 +1,244 @@
require File.dirname(__FILE__) + '/spec'
class Object
class << self
# Lookup missing generators using const_missing. This allows any
# generator to reference another without having to know its location:
# RubyGems, ~/.rails/generators, and RAILS_ROOT/generators.
def lookup_missing_generator(class_id)
if md = /(.+)Generator$/.match(class_id.to_s)
name = md.captures.first.demodulize.underscore
Rails::Generator::Base.lookup(name).klass
else
const_missing_before_generators(class_id)
end
end
unless respond_to?(:const_missing_before_generators)
alias_method :const_missing_before_generators, :const_missing
alias_method :const_missing, :lookup_missing_generator
end
end
end
# User home directory lookup adapted from RubyGems.
def Dir.user_home
if ENV['HOME']
ENV['HOME']
elsif ENV['USERPROFILE']
ENV['USERPROFILE']
elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
"#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
else
File.expand_path '~'
end
end
module Rails
module Generator
# Generator lookup is managed by a list of sources which return specs
# describing where to find and how to create generators. This module
# provides class methods for manipulating the source list and looking up
# generator specs, and an #instance wrapper for quickly instantiating
# generators by name.
#
# A spec is not a generator: it's a description of where to find
# the generator and how to create it. A source is anything that
# yields generators from #each. PathSource and GemGeneratorSource are provided.
module Lookup
def self.included(base)
base.extend(ClassMethods)
base.use_component_sources!
end
# Convenience method to instantiate another generator.
def instance(generator_name, args, runtime_options = {})
self.class.instance(generator_name, args, runtime_options)
end
module ClassMethods
# The list of sources where we look, in order, for generators.
def sources
read_inheritable_attribute(:sources) or use_component_sources!
end
# Add a source to the end of the list.
def append_sources(*args)
sources.concat(args.flatten)
invalidate_cache!
end
# Add a source to the beginning of the list.
def prepend_sources(*args)
write_inheritable_array(:sources, args.flatten + sources)
invalidate_cache!
end
# Reset the source list.
def reset_sources
write_inheritable_attribute(:sources, [])
invalidate_cache!
end
# Use application generators (app, ?).
def use_application_sources!
reset_sources
sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
end
# Use component generators (model, controller, etc).
# 1. Rails application. If RAILS_ROOT is defined we know we're
# generating in the context of a Rails application, so search
# RAILS_ROOT/generators.
# 2. Look in plugins, either for generators/ or rails_generators/
# directories within each plugin
# 3. User home directory. Search ~/.rails/generators.
# 4. RubyGems. Search for gems named *_generator, and look for
# generators within any RubyGem's
# /rails_generators/<generator_name>_generator.rb file.
# 5. Builtins. Model, controller, mailer, scaffold, and so on.
def use_component_sources!
reset_sources
if defined? ::RAILS_ROOT
sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators")
sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators")
sources << PathSource.new(:plugins, "#{::RAILS_ROOT}/vendor/plugins/*/**/generators")
sources << PathSource.new(:plugins, "#{::RAILS_ROOT}/vendor/plugins/*/**/rails_generators")
end
sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
if Object.const_defined?(:Gem)
sources << GemGeneratorSource.new
sources << GemPathSource.new
end
sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
end
# Lookup knows how to find generators' Specs from a list of Sources.
# Searches the sources, in order, for the first matching name.
def lookup(generator_name)
@found ||= {}
generator_name = generator_name.to_s.downcase
@found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
unless @found[generator_name]
chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
rx = /^#{chars}$/
gns = cache.select{|spec| spec.name =~ rx }
@found[generator_name] ||= gns.first if gns.length == 1
raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
end
@found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
end
# Convenience method to lookup and instantiate a generator.
def instance(generator_name, args = [], runtime_options = {})
lookup(generator_name).klass.new(args, full_options(runtime_options))
end
private
# Lookup and cache every generator from the source list.
def cache
@cache ||= sources.inject([]) { |cache, source| cache + source.map }
end
# Clear the cache whenever the source list changes.
def invalidate_cache!
@cache = nil
end
end
end
# Sources enumerate (yield from #each) generator specs which describe
# where to find and how to create generators. Enumerable is mixed in so,
# for example, source.collect will retrieve every generator.
# Sources may be assigned a label to distinguish them.
class Source
include Enumerable
attr_reader :label
def initialize(label)
@label = label
end
# The each method must be implemented in subclasses.
# The base implementation raises an error.
def each
raise NotImplementedError
end
# Return a convenient sorted list of all generator names.
def names
map { |spec| spec.name }.sort
end
end
# PathSource looks for generators in a filesystem directory.
class PathSource < Source
attr_reader :path
def initialize(label, path)
super label
@path = path
end
# Yield each eligible subdirectory.
def each
Dir["#{path}/[a-z]*"].each do |dir|
if File.directory?(dir)
yield Spec.new(File.basename(dir), dir, label)
end
end
end
end
class AbstractGemSource < Source
def initialize
super :RubyGems
end
end
# GemGeneratorSource hits the mines to quarry for generators. The latest versions
# of gems named *_generator are selected.
class GemGeneratorSource < AbstractGemSource
# Yield latest versions of generator gems.
def each
Gem::cache.search(/_generator$/).inject({}) { |latest, gem|
hem = latest[gem.name]
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
latest
}.values.each { |gem|
yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
}
end
end
# GemPathSource looks for generators within any RubyGem's /rails_generators/<generator_name>_generator.rb file.
class GemPathSource < AbstractGemSource
# Yield each generator within rails_generator subdirectories.
def each
generator_full_paths.each do |generator|
yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label)
end
end
private
def generator_full_paths
@generator_full_paths ||=
Gem::cache.inject({}) do |latest, name_gem|
name, gem = name_gem
hem = latest[gem.name]
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
latest
end.values.inject([]) do |mem, gem|
Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator|
mem << generator
end
mem
end
end
end
end
end

View file

@ -0,0 +1,53 @@
module Rails
module Generator
# Manifest captures the actions a generator performs. Instantiate
# a manifest with an optional target object, hammer it with actions,
# then replay or rewind on the object of your choice.
#
# Example:
# manifest = Manifest.new { |m|
# m.make_directory '/foo'
# m.create_file '/foo/bar.txt'
# }
# manifest.replay(creator)
# manifest.rewind(destroyer)
class Manifest
attr_reader :target
# Take a default action target. Yield self if block given.
def initialize(target = nil)
@target, @actions = target, []
yield self if block_given?
end
# Record an action.
def method_missing(action, *args, &block)
@actions << [action, args, block]
end
# Replay recorded actions.
def replay(target = nil)
send_actions(target || @target, @actions)
end
# Rewind recorded actions.
def rewind(target = nil)
send_actions(target || @target, @actions.reverse)
end
# Erase recorded actions.
def erase
@actions = []
end
private
def send_actions(target, actions)
actions.each do |method, args, block|
target.send(method, *args, &block)
end
end
end
end
end

View file

@ -0,0 +1,143 @@
require 'optparse'
module Rails
module Generator
module Options
def self.included(base)
base.extend(ClassMethods)
class << base
if respond_to?(:inherited)
alias_method :inherited_without_options, :inherited
end
alias_method :inherited, :inherited_with_options
end
end
module ClassMethods
def inherited_with_options(sub)
inherited_without_options(sub) if respond_to?(:inherited_without_options)
sub.extend(Rails::Generator::Options::ClassMethods)
end
def mandatory_options(options = nil)
if options
write_inheritable_attribute(:mandatory_options, options)
else
read_inheritable_attribute(:mandatory_options) or write_inheritable_attribute(:mandatory_options, {})
end
end
def default_options(options = nil)
if options
write_inheritable_attribute(:default_options, options)
else
read_inheritable_attribute(:default_options) or write_inheritable_attribute(:default_options, {})
end
end
# Merge together our class options. In increasing precedence:
# default_options (class default options)
# runtime_options (provided as argument)
# mandatory_options (class mandatory options)
def full_options(runtime_options = {})
default_options.merge(runtime_options).merge(mandatory_options)
end
end
# Each instance has an options hash that's populated by #parse.
def options
@options ||= {}
end
attr_writer :options
protected
# Convenient access to class mandatory options.
def mandatory_options
self.class.mandatory_options
end
# Convenient access to class default options.
def default_options
self.class.default_options
end
# Merge together our instance options. In increasing precedence:
# default_options (class default options)
# options (instance options)
# runtime_options (provided as argument)
# mandatory_options (class mandatory options)
def full_options(runtime_options = {})
self.class.full_options(options.merge(runtime_options))
end
# Parse arguments into the options hash. Classes may customize
# parsing behavior by overriding these methods:
# #banner Usage: ./script/generate [options]
# #add_options! Options:
# some options..
# #add_general_options! General Options:
# general options..
def parse!(args, runtime_options = {})
self.options = {}
@option_parser = OptionParser.new do |opt|
opt.banner = banner
add_options!(opt)
add_general_options!(opt)
opt.parse!(args)
end
return args
ensure
self.options = full_options(runtime_options)
end
# Raise a usage error. Override usage_message to provide a blurb
# after the option parser summary.
def usage(message = usage_message)
raise UsageError, "#{@option_parser}\n#{message}"
end
def usage_message
''
end
# Override with your own usage banner.
def banner
"Usage: #{$0} [options]"
end
# Override to add your options to the parser:
# def add_options!(opt)
# opt.on('-v', '--verbose') { |value| options[:verbose] = value }
# end
def add_options!(opt)
end
# Adds general options like -h and --quiet. Usually don't override.
def add_general_options!(opt)
opt.separator ''
opt.separator 'Rails Info:'
opt.on('-v', '--version', 'Show the Rails version number and quit.')
opt.on('-h', '--help', 'Show this help message and quit.') { |v| options[:help] = v }
opt.separator ''
opt.separator 'General Options:'
opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v }
opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force }
opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip }
opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v }
opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v }
opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do
options[:svn] = `svn status`.inject({}) do |opt, e|
opt[e.chomp[7..-1]] = true
opt
end
end
end
end
end
end

View file

@ -0,0 +1,86 @@
require File.dirname(__FILE__) + '/options'
module Rails
module Generator
module Scripts
# Generator scripts handle command-line invocation. Each script
# responds to an invoke! class method which handles option parsing
# and generator invocation.
class Base
include Options
default_options :collision => :ask, :quiet => false
# Run the generator script. Takes an array of unparsed arguments
# and a hash of parsed arguments, takes the generator as an option
# or first remaining argument, and invokes the requested command.
def run(args = [], runtime_options = {})
begin
parse!(args.dup, runtime_options)
rescue OptionParser::InvalidOption => e
# Don't cry, script. Generators want what you think is invalid.
end
# Generator name is the only required option.
unless options[:generator]
usage if args.empty?
options[:generator] ||= args.shift
end
# Look up generator instance and invoke command on it.
Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
rescue => e
puts e
puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace]
raise SystemExit
end
protected
# Override with your own script usage banner.
def banner
"Usage: #{$0} generator [options] [args]"
end
def usage_message
usage = "\nInstalled Generators\n"
Rails::Generator::Base.sources.inject({}) do |mem, source|
label = source.label.to_s.capitalize
mem[label] ||= []
mem[label] |= source.names
mem
end.each_pair do |label, names|
usage << " #{label}: #{names.join(', ')}\n" unless names.empty?
end
usage << <<end_blurb
More are available at http://rubyonrails.org/show/Generators
1. Download, for example, login_generator.zip
2. Unzip to directory #{Dir.user_home}/.rails/generators/login
to use the generator with all your Rails apps
end_blurb
if Object.const_defined?(:RAILS_ROOT)
usage << <<end_blurb
or to #{File.expand_path(RAILS_ROOT)}/lib/generators/login
to use with this app only.
end_blurb
end
usage << <<end_blurb
3. Run generate with no arguments for usage information
#{$0} login
Generator gems are also available:
1. gem search -r generator
2. gem install login_generator
3. #{$0} login
end_blurb
return usage
end
end # Base
end
end
end

View file

@ -0,0 +1,30 @@
require File.dirname(__FILE__) + '/../scripts'
module Rails::Generator::Scripts
class Destroy < Base
mandatory_options :command => :destroy
protected
def usage_message
usage = "\nInstalled Generators\n"
Rails::Generator::Base.sources.each do |source|
label = source.label.to_s.capitalize
names = source.names
usage << " #{label}: #{names.join(', ')}\n" unless names.empty?
end
usage << <<end_blurb
This script will destroy all files created by the corresponding
script/generate command. For instance, script/destroy migration CreatePost
will delete the appropriate ###_create_post.rb file in db/migrate, while
script/destroy scaffold Post will delete the posts controller and
views, post model and migration, all associated tests, and the map.resources
:posts line in config/routes.rb.
For instructions on finding new generators, run script/generate
end_blurb
return usage
end
end
end

View file

@ -0,0 +1,7 @@
require File.dirname(__FILE__) + '/../scripts'
module Rails::Generator::Scripts
class Generate < Base
mandatory_options :command => :create
end
end

View file

@ -0,0 +1,12 @@
require File.dirname(__FILE__) + '/../scripts'
module Rails::Generator::Scripts
class Update < Base
mandatory_options :command => :update
protected
def banner
"Usage: #{$0} [options] scaffold"
end
end
end

View file

@ -0,0 +1,160 @@
# A class for creating random secret keys. This class will do its best to create a
# random secret key that's as secure as possible, using whatever methods are
# available on the current platform. For example:
#
# generator = Rails::SecretKeyGenerator("some unique identifier, such as the application name")
# generator.generate_secret # => "f3f1be90053fa851... (some long string)"
module Rails
class SecretKeyGenerator
GENERATORS = [ :secure_random, :win32_api, :urandom, :openssl, :prng ].freeze
def initialize(identifier)
@identifier = identifier
end
# Generate a random secret key with the best possible method available on
# the current platform.
def generate_secret
generator = GENERATORS.find do |g|
self.class.send("supports_#{g}?")
end
send("generate_secret_with_#{generator}")
end
# Generate a random secret key by using the Win32 API. Raises LoadError
# if the current platform cannot make use of the Win32 API. Raises
# SystemCallError if some other error occured.
def generate_secret_with_win32_api
# Following code is based on David Garamond's GUID library for Ruby.
require 'Win32API'
crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext",
'PPPII', 'L')
crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom",
'LIP', 'L')
crypt_release_context = Win32API.new("advapi32", "CryptReleaseContext",
'LI', 'L')
prov_rsa_full = 1
crypt_verifycontext = 0xF0000000
hProvStr = " " * 4
if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full,
crypt_verifycontext) == 0
raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
end
hProv, = hProvStr.unpack('L')
bytes = " " * 64
if crypt_gen_random.call(hProv, bytes.size, bytes) == 0
raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
end
if crypt_release_context.call(hProv, 0) == 0
raise SystemCallError, "CryptReleaseContext failed: #{lastWin32ErrorMessage}"
end
bytes.unpack("H*")[0]
end
# Generate a random secret key with Ruby 1.9's SecureRandom module.
# Raises LoadError if the current Ruby version does not support
# SecureRandom.
def generate_secret_with_secure_random
require 'securerandom'
return SecureRandom.hex(64)
end
# Generate a random secret key with OpenSSL. If OpenSSL is not
# already loaded, then this method will attempt to load it.
# LoadError will be raised if that fails.
def generate_secret_with_openssl
require 'openssl'
if !File.exist?("/dev/urandom")
# OpenSSL transparently seeds the random number generator with
# data from /dev/urandom. On platforms where that is not
# available, such as Windows, we have to provide OpenSSL with
# our own seed. Unfortunately there's no way to provide a
# secure seed without OS support, so we'll have to do with
# rand() and Time.now.usec().
OpenSSL::Random.seed(rand(0).to_s + Time.now.usec.to_s)
end
data = OpenSSL::BN.rand(2048, -1, false).to_s
return OpenSSL::Digest::SHA512.new(data).hexdigest
end
# Generate a random secret key with /dev/urandom.
# Raises SystemCallError on failure.
def generate_secret_with_urandom
return File.read("/dev/urandom", 64).unpack("H*")[0]
end
# Generate a random secret key with Ruby's pseudo random number generator,
# as well as some environment information.
#
# This is the least cryptographically secure way to generate a secret key,
# and should be avoided whenever possible.
def generate_secret_with_prng
require 'digest/sha2'
sha = Digest::SHA2.new(512)
now = Time.now
sha << now.to_s
sha << String(now.usec)
sha << String(rand(0))
sha << String($$)
sha << @identifier
return sha.hexdigest
end
private
def lastWin32ErrorMessage
# Following code is based on David Garamond's GUID library for Ruby.
get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
format_message = Win32API.new("kernel32", "FormatMessageA",
'LPLLPLPPPPPPPP', 'L')
format_message_ignore_inserts = 0x00000200
format_message_from_system = 0x00001000
code = get_last_error.call
msg = "\0" * 1024
len = format_message.call(format_message_ignore_inserts +
format_message_from_system, 0,
code, 0, msg, 1024, nil, nil,
nil, nil, nil, nil, nil, nil)
msg[0, len].tr("\r", '').chomp
end
def self.supports_secure_random?
begin
require 'securerandom'
true
rescue LoadError
false
end
end
def self.supports_win32_api?
return false unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
begin
require 'Win32API'
true
rescue LoadError
false
end
end
def self.supports_urandom?
File.exist?('/dev/urandom')
end
def self.supports_openssl?
begin
require 'openssl'
true
rescue LoadError
false
end
end
def self.supports_prng?
true
end
end
end

View file

@ -0,0 +1,46 @@
module Rails
module Generator
class SimpleLogger # :nodoc:
attr_reader :out
attr_accessor :quiet
def initialize(out = $stdout)
@out = out
@quiet = false
@level = 0
end
def log(status, message, &block)
@out.print("%12s %s%s\n" % [status, ' ' * @level, message]) unless quiet
indent(&block) if block_given?
end
def indent(&block)
@level += 1
if block_given?
begin
block.call
ensure
outdent
end
end
end
def outdent
@level -= 1
if block_given?
begin
block.call
ensure
indent
end
end
end
private
def method_missing(method, *args, &block)
log(method.to_s, args.first, &block)
end
end
end
end

View file

@ -0,0 +1,44 @@
module Rails
module Generator
# A spec knows where a generator was found and how to instantiate it.
# Metadata include the generator's name, its base path, and the source
# which yielded it (PathSource, GemPathSource, etc.)
class Spec
attr_reader :name, :path, :source
def initialize(name, path, source)
@name, @path, @source = name, path, source
end
# Look up the generator class. Require its class file, find the class
# in ObjectSpace, tag it with this spec, and return.
def klass
unless @klass
require class_file
@klass = lookup_class
@klass.spec = self
end
@klass
end
def class_file
"#{path}/#{name}_generator.rb"
end
def class_name
"#{name.camelize}Generator"
end
private
# Search for the first Class descending from Rails::Generator::Base
# whose name matches the requested class name.
def lookup_class
ObjectSpace.each_object(Class) do |obj|
return obj if obj.ancestors.include?(Rails::Generator::Base) and
obj.name.split('::').last == class_name
end
raise NameError, "Missing #{class_name} class in #{class_file}"
end
end
end
end