aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-10-28 21:21:07 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-10-28 21:21:07 +0000
commit0a735ca4b3bd28dc7760b3db816d13ddc6db56a5 (patch)
treed0e59f43ca0ece9eaaa619269562ed0958b5ae14
parent419794d8ba1915d63126584546dfa8c9c084f4a0 (diff)
downloadrails-0a735ca4b3bd28dc7760b3db816d13ddc6db56a5.tar.gz
rails-0a735ca4b3bd28dc7760b3db816d13ddc6db56a5.tar.bz2
rails-0a735ca4b3bd28dc7760b3db816d13ddc6db56a5.zip
Added script/plugin to manage plugins (install, remove, list, etc) [Ryan Tomayko]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2793 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rw-r--r--railties/CHANGELOG2
-rw-r--r--railties/Rakefile2
-rw-r--r--railties/bin/plugin3
-rw-r--r--railties/lib/commands/plugin.rb745
-rw-r--r--railties/lib/rails_generator/generators/applications/app/app_generator.rb2
5 files changed, 751 insertions, 3 deletions
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index ac9e96d7fe..e1b2fc5447 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,6 +1,6 @@
*SVN*
-* Added Rakefile to plugin generator. [Jeremy Kemper]
+* Added script/plugin to manage plugins (install, remove, list, etc) [Ryan Tomayko]
* Added test_plugins task: Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name) [DHH]
diff --git a/railties/Rakefile b/railties/Rakefile
index 511ebfe744..c3fc2bb72f 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -38,7 +38,7 @@ HTML_FILES = %w( 404.html 500.html index.html robots.txt favicon.ico
javascripts/prototype.js javascripts/scriptaculous.js
javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js
javascripts/slider.js )
-BIN_FILES = %w( breakpointer console destroy generate performance/benchmarker performance/profiler process/reaper process/spawner process/spinner runner server lighttpd )
+BIN_FILES = %w( breakpointer console destroy generate performance/benchmarker performance/profiler process/reaper process/spawner process/spinner runner server lighttpd plugin )
VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport actionwebservice railties )
diff --git a/railties/bin/plugin b/railties/bin/plugin
new file mode 100644
index 0000000000..d3c4ba40c8
--- /dev/null
+++ b/railties/bin/plugin
@@ -0,0 +1,3 @@
+#!/usr/local/bin/ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/plugin' \ No newline at end of file
diff --git a/railties/lib/commands/plugin.rb b/railties/lib/commands/plugin.rb
new file mode 100644
index 0000000000..3089c56797
--- /dev/null
+++ b/railties/lib/commands/plugin.rb
@@ -0,0 +1,745 @@
+#!/usr/local/bin/ruby
+
+# Rails Plugin Manager.
+#
+# Listing available plugins:
+#
+# $ ./script/plugin list
+# continuous_builder http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder
+# asset_timestamping http://svn.aviditybytes.com/rails/plugins/asset_timestamping
+# enumerations_mixin http://svn.protocool.com/rails/plugins/enumerations_mixin/trunk
+# calculations http://techno-weenie.net/svn/projects/calculations/
+# ...
+#
+# Installing plugins:
+#
+# $ ./script/plugin install continuous_builder asset_timestamping
+#
+# Finding Repositories:
+#
+# $ ./script/plugin discover
+#
+# Adding Repositories:
+#
+# $ ./script/plugin source http://svn.protocool.com/rails/plugins/
+#
+# How it works:
+#
+# * Maintains a list of subversion repositories that are assumed to have
+# a plugin directory structure. Manage them with the (source, unsource,
+# and sources commands)
+#
+# * The discover command scrapes the following page for things that
+# look like subversion repositories with plugins:
+# http://wiki.rubyonrails.org/rails/pages/Plugins
+#
+# * If `vendor/plugins` is under subversion control, the script will
+# modify the svn:externals property and perform an update. You can
+# use normal subversion commands to keep the plugins up to date or
+# you can use the built in update command.
+#
+# * Or, if `vendor/plugins` is not under subversion control, the
+# plugin is pulled via `svn checkout` or `svn export` but looks
+# exactly the same.
+#
+# This is Free Software, copyright 2005 by Ryan Tomayko and is licensed
+# MIT: (http://www.opensource.org/licenses/mit-license.php)
+#
+# Send patches to rtomayko@gmail.com
+
+$verbose = false
+
+require 'fileutils'
+require 'tempfile'
+
+include FileUtils
+
+class RailsEnvironment
+ attr_reader :root
+
+ def initialize(dir)
+ @root = dir
+ end
+
+ def self.find(dir=nil)
+ dir ||= pwd
+ while dir.length > 1
+ return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb'))
+ dir = File.dirname(dir)
+ end
+ end
+
+ def self.default
+ @default ||= find
+ end
+
+ def self.default=(rails_env)
+ @default = rails_env
+ end
+
+ def install(name_uri_or_plugin)
+ if name_uri_or_plugin.is_a? String
+ if name_uri_or_plugin =~ /:\/\//
+ plugin = Plugin.new(name_uri_or_plugin)
+ else
+ plugin = Plugins[name_uri_or_plugin]
+ end
+ else
+ plugin = name_uri_or_plugin
+ end
+ unless plugin.nil?
+ plugin.install
+ else
+ puts "plugin not found: #{name_uri_or_plugin}"
+ end
+ end
+
+ def use_externals?
+ File.directory?("#{root}/vendor/plugins/.svn")
+ end
+
+ def use_checkout?
+ # this is a bit of a guess. we assume that if the rails environment
+ # is under subversion than they probably want the plugin checked out
+ # instead of exported. This can be overridden on the command line
+ File.directory?("#{root}/.svn")
+ end
+
+ def best_install_method
+ case
+ when use_externals? then :externals
+ when use_checkout? then :checkout
+ else :export
+ end
+ end
+
+ def externals
+ return [] unless use_externals?
+ ext = `svn propget svn:externals #{root}/vendor/plugins 2> /dev/null`
+ ext.reject{ |line| line.strip == '' }.map do |line|
+ line.strip.split(/\s+/, 2)
+ end
+ end
+
+ def externals=(items)
+ unless items.is_a? String
+ items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n")
+ end
+ Tempfile.open("svn-set-prop") do |file|
+ file.write(items)
+ file.flush
+ system("svn propset svn:externals -F #{file.path} #{root}/vendor/plugins")
+ end
+ end
+
+end
+
+class Plugin
+ attr_reader :name, :uri
+
+ def initialize(uri, name=nil)
+ @uri = uri
+ guess_name(uri)
+ end
+
+ def to_s
+ "#{@name.ljust(30)}#{@uri}"
+ end
+
+ def installed?
+ File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \
+ or rails_env.externals.detect{ |name, repo| self.uri == repo }
+ end
+
+ def install(method=nil)
+ method ||= rails_env.best_install_method?
+ unless installed?
+ send("install_using_#{method}")
+ else
+ puts "already installed: #{name} (#{uri})"
+ end
+ end
+
+ private
+ def install_using_export
+ root = rails_env.root
+ mkdir_p "#{root}/vendor/plugins"
+ system("svn export #{uri} #{root}/vendor/plugins/#{name}")
+ end
+
+ def install_using_checkout
+ root = rails_env.root
+ mkdir_p "#{root}/vendor/plugins"
+ system("svn checkout #{uri} #{root}/vendor/plugins/#{name}")
+ end
+
+ def install_using_externals
+ externals = rails_env.externals
+ externals.push([@name, uri])
+ rails_env.externals = externals
+ install_using_checkout
+ end
+
+ def guess_name(url)
+ @name = File.basename(url)
+ if @name == 'trunk' || @name.empty?
+ @name = File.basename(File.dirname(url))
+ end
+ end
+
+ def rails_env
+ @rails_env || RailsEnvironment.default
+ end
+end
+
+class Repositories
+ include Enumerable
+
+ def initialize(cache_file="~/.rails-plugin-sources")
+ @cache_file = File.expand_path(cache_file)
+ load!
+ end
+
+ def each(&block)
+ @repositories.each(&block)
+ end
+
+ def add(uri)
+ unless find{|repo| repo.uri == uri }
+ @repositories.push(Repository.new(uri)).last
+ end
+ end
+
+ def remove(uri)
+ @repositories.reject!{|repo| repo.uri == uri}
+ end
+
+ def exist?(uri)
+ @repositories.detect{|repo| repo.uri == uri }
+ end
+
+ def all
+ @repositories
+ end
+
+ def find_plugin(name)
+ @repositories.each do |repo|
+ repo.each do |plugin|
+ return plugin if plugin.name == name
+ end
+ end
+ end
+
+ def load!
+ contents = File.exist?(@cache_file) ? File.read(@cache_file) : defaults
+ contents = defaults if contents.empty?
+ @repositories = contents.split(/\n/).reject do |line|
+ line =~ /^\s*#/ or line =~ /^\s*$/
+ end.map { |source| Repository.new(source.strip) }
+ end
+
+ def save
+ File.open(@cache_file, 'w') do |f|
+ each do |repo|
+ f.write(repo.uri)
+ f.write("\n")
+ end
+ end
+ end
+
+ def defaults
+ <<-REPOSITORIES
+ http://dev.rubyonrails.org/svn/rails/plugins/
+ http://lesscode.org/svn/rtomayko/rails/plugins/
+ http://svn.aviditybytes.com/rails/plugins/
+ REPOSITORIES
+ end
+
+ def self.instance
+ @instance ||= Repositories.new
+ end
+
+ def self.each(&block)
+ self.instance.each(&block)
+ end
+end
+
+class Repository
+ include Enumerable
+ attr_reader :uri, :plugins
+
+ def initialize(uri)
+ uri << "/" unless uri =~ /\/$/
+ @uri = uri
+ @plugins = nil
+ end
+
+ def plugins
+ unless @plugins
+ if $verbose
+ puts "Discovering plugins in #{@uri}"
+ puts index
+ end
+
+ @plugins = index.split(/\n/).reject{ |line| line !~ /\/$/ }
+ @plugins.map! { |name| Plugin.new(File.join(@uri, name), name) }
+ end
+
+ @plugins
+ end
+
+ def each(&block)
+ plugins.each(&block)
+ end
+
+ private
+ def index
+ @index ||= `svn ls #{@uri}`
+ end
+end
+
+
+# load default environment and parse arguments
+require 'optparse'
+module Commands
+
+ class Plugin
+ attr_reader :environment, :script_name, :sources
+ def initialize
+ @environment = RailsEnvironment.default
+ @rails_root = RailsEnvironment.default.root
+ @script_name = File.basename($0)
+ @sources = []
+ end
+
+ def environment=(value)
+ @environment = value
+ RailsEnvironment.default = value
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@script_name} [OPTIONS] command"
+ o.define_head "Rails plugin manager."
+
+ o.separator ""
+ o.separator "GENERAL OPTIONS"
+
+ o.on("-r", "--root=DIR", String,
+ "Set an explicit rails app directory.",
+ "Default: #{@rails_root}") { |@rails_root| self.environment = RailsEnvironment.new(@rails_root) }
+ o.on("-s", "--source=URL1,URL2", Array,
+ "Use the specified plugin repositories instead of the defaults.") { |@sources|}
+
+ o.on("-v", "--verbose", "Turn on verbose output.") { |$verbose| }
+ o.on("-h", "--help", "Show this help message.") { puts o; exit }
+
+ o.separator ""
+ o.separator "COMMANDS"
+
+ o.separator " discover Discover plugin repositories."
+ o.separator " list List available plugins."
+ o.separator " install Install plugin(s) from known repositories or URLs."
+ o.separator " update Update installed plugins."
+ o.separator " remove Uninstall plugins."
+ o.separator " source Add a plugin source repository."
+ o.separator " unsource Remove a plugin repository."
+ o.separator " sources List currently configured plugin repositories."
+
+ o.separator ""
+ o.separator "EXAMPLES"
+ o.separator " Install a plugin:"
+ o.separator " #{@script_name} install continuous_builder\n"
+ o.separator " Install a plugin from a subversion URL:"
+ o.separator " #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n"
+ o.separator " Install a plugin from a subversion URL:"
+ o.separator " #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n"
+ o.separator " List all available plugins:"
+ o.separator " #{@script_name} list\n"
+ o.separator " List plugins in the specified repository:"
+ o.separator " #{@script_name} list --source=http://dev.rubyonrails.com/svn/rails/plugins/\n"
+ o.separator " Discover and prompt to add new repositories:"
+ o.separator " #{@script_name} discover\n"
+ o.separator " Discover new repositories but just list them, don't add anything:"
+ o.separator " #{@script_name} discover -l\n"
+ o.separator " Add a new repository to the source list:"
+ o.separator " #{@script_name} source http://dev.rubyonrails.com/svn/rails/plugins/\n"
+ end
+ end
+
+ def parse!(args=ARGV)
+ general, sub = split_args(args)
+ options.parse!(general)
+
+ command = general.shift
+ if command =~ /^(list|discover|install|source|unsource|sources|remove|update)$/
+ command = Commands.const_get(command.capitalize).new(self)
+ command.parse!(sub)
+ else
+ puts "Unknown command: #{command}"
+ puts "Try: #{$0} --help"
+ exit 1
+ end
+ end
+
+ def split_args(args)
+ left = []
+ left << args.shift while args[0] and args[0] =~ /^-/
+ left << args.shift if args[0]
+ return [left, args]
+ end
+
+ def self.parse!(args=ARGV)
+ Plugin.new.parse!(args)
+ end
+ end
+
+
+ class List
+ def initialize(base_command)
+ @base_command = base_command
+ @sources = []
+ @local = false
+ @remote = true
+ @details = false
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} list [OPTIONS] [PATTERN]"
+ o.define_head "List available plugins."
+ o.separator ""
+ o.separator "Options:"
+ o.separator ""
+ o.on( "-s", "--source=URL1,URL2", Array,
+ "Use the specified plugin repositories.") {|@sources|}
+ o.on( "--local",
+ "List locally installed plugins.") {|@local| @remote = false}
+ o.on( "--remote",
+ "List remotely availabled plugins. This is the default behavior",
+ "unless --local is provided.") {|@remote|}
+ o.on( "-l", "--long",
+ "Long listing / details about each plugin.") {|@details|}
+ end
+ end
+
+ def parse!(args)
+ options.order!(args)
+ unless @sources.empty?
+ @sources.map!{ |uri| Repository.new(uri) }
+ else
+ @sources = Repositories.instance.all
+ end
+ if @remote
+ @sources.map{|r| r.plugins}.flatten.each do |plugin|
+ if @local or !plugin.installed?
+ puts plugin.to_s
+ system "svn info #{plugin.uri}" if @details
+ end
+ end
+ else
+ cd "#{@base_command.environment.root}/vendor/plugins"
+ Dir["*"].select{|p| File.directory?(p)}.each do |name|
+ puts name
+ system "svn info #{name}" if @details
+ end
+ end
+ end
+ end
+
+
+ class Sources
+ def initialize(base_command)
+ @base_command = base_command
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} sources [OPTIONS] [PATTERN]"
+ o.define_head "List configured plugin repositories."
+ o.separator ""
+ o.separator "Options:"
+ o.separator ""
+ o.on( "-c", "--check",
+ "Report status of repository.") { |@sources|}
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ Repositories.each do |repo|
+ puts repo.uri
+ end
+ end
+ end
+
+
+ class Source
+ def initialize(base_command)
+ @base_command = base_command
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} source REPOSITORY"
+ o.define_head "Add a new repository."
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ count = 0
+ args.each do |uri|
+ if Repositories.instance.add(uri)
+ puts "added: #{uri.ljust(50)}" if $verbose
+ count += 1
+ else
+ puts "failed: #{uri.ljust(50)}"
+ end
+ end
+ Repositories.instance.save
+ puts "Added #{count} repositories."
+ end
+ end
+
+
+ class Unsource
+ def initialize(base_command)
+ @base_command = base_command
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} source URI [URI [URI]...]"
+ o.define_head "Remove repositories from the default search list."
+ o.separator ""
+ o.on_tail("-h", "--help", "Show this help message.") { puts o; exit }
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ count = 0
+ args.each do |uri|
+ if Repositories.instance.remove(uri)
+ count += 1
+ puts "removed: #{uri.ljust(50)}"
+ else
+ puts "failed: #{uri.ljust(50)}"
+ end
+ end
+ Repositories.instance.save
+ puts "Removed #{count} repositories."
+ end
+ end
+
+
+ class Discover
+ def initialize(base_command)
+ @base_command = base_command
+ @list = false
+ @prompt = true
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} discover URI [URI [URI]...]"
+ o.define_head "Discover repositories referenced on a page."
+ o.separator ""
+ o.separator "Options:"
+ o.separator ""
+ o.on( "-l", "--list",
+ "List but don't prompt or add discovered repositories.") { |@list| @prompt = !@list }
+ o.on( "-n", "--no-prompt",
+ "Add all new repositories without prompting.") { |v| @prompt = !v }
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ args = ['http://wiki.rubyonrails.org/rails/pages/Plugins'] if args.empty?
+ args.each do |uri|
+ scrape(uri) do |repo_uri|
+ catch(:next_uri) do
+ if @prompt
+ begin
+ $stdout.print "Add #{repo_uri}? [Y/n] "
+ throw :next_uri if $stdin.gets !~ /^y?$/i
+ rescue Interrupt
+ $stdout.puts
+ exit 1
+ end
+ elsif @list
+ puts repo_uri
+ throw :next_uri
+ end
+ Repositories.instance.add(repo_uri)
+ puts "discovered: #{repo_uri}" if $verbose or !@prompt
+ end
+ end
+ end
+ Repositories.instance.save
+ end
+
+ def scrape(uri)
+ require 'open-uri'
+ puts "Scraping #{uri}" if $verbose
+ dupes = []
+ content = open(uri).each do |line|
+ if line =~ /<a[^>]*href=['"]([^'"]*)['"]/
+ uri = $1
+ if uri =~ /\/plugins\// and uri !~ /\/browser\//
+ uri = extract_repository_uri(uri)
+ yield uri unless dupes.include?(uri) or Repositories.instance.exist?(uri)
+ dupes << uri
+ end
+ end
+ end
+ end
+
+ def extract_repository_uri(uri)
+ uri.match(/(svn|https?):.*\/plugins\//i)[0]
+ end
+ end
+
+ class Install
+ def initialize(base_command)
+ @base_command = base_command
+ @externals = false
+ @checkout = false
+ @export = false
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]"
+ o.define_head "Install one or more plugins."
+ o.separator ""
+ o.separator "Options:"
+ o.on( "-e", "--externals",
+ "Use svn:externals. This is the default behavior when",
+ "vendor/plugins is under subversion control.") { |@externals| }
+ o.on( "-o", "--checkout",
+ "Use checkout. This is turns externals off and is the default",
+ "behavior when vendor/plugins is not under subversion control.") { |@checkout| }
+ o.on( "-x", "--export",
+ "Use export. This turns externals support off and is the default",
+ "behavior when the current project is not under subversion control.") { |@export| }
+ o.separator ""
+ o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to "
+ o.separator "a plugin repository."
+ end
+ end
+
+ def determine_install_method
+ requested = case
+ when @export then method = :export
+ when @checkout then method = :checkout
+ else method = :externals
+ end
+ best = @base_command.environment.best_install_method
+ if best == :export and requested != :export
+ puts "Cannot install using #{requested} because this project is not under subversion."
+ exit 1
+ end
+ if best == :checkout and @externals
+ puts "Cannot install using externals because vendor/plugins is not under subversion."
+ exit 1
+ end
+ requested
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ environment = @base_command.environment
+ install_method = determine_install_method
+ puts "Plugins will be installed using #{install_method}" if $verbose
+ args.each do |name|
+ if name =~ /\// then
+ Plugin.new(name).install(install_method)
+ else
+ plugin = Repositories.instance.find_plugin(name)
+ unless plugin.nil?
+ plugin.install(install_method)
+ else
+ puts "Plugin not found: #{name}"
+ exit 1
+ end
+ end
+ end
+ end
+ end
+
+
+ class Remove
+ def initialize(base_command)
+ @base_command = base_command
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} remove name [name]..."
+ o.define_head "Remove plugins."
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ root = @base_command.environment.root
+ args.each do |name|
+ path = "#{root}/vendor/plugins/#{name}"
+ if File.directory?(path)
+ rm_r path
+ else
+ puts "Plugin doesn't exist: #{path}"
+ end
+ # clean up svn:externals
+ externals = @base_command.environment.externals
+ externals.reject!{|n,u| name == n or name == u}
+ @base_command.environment.externals = externals
+ end
+ end
+ end
+
+ class Update
+ def initialize(base_command)
+ @base_command = base_command
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} update [name [name]...]"
+ o.define_head "Update plugins."
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ root = @base_command.environment.root
+ args = Dir["#{root}/vendor/plugins/*"].map do |f|
+ File.directory?("#{f}/.svn") ? File.basename(f) : nil
+ end.compact if args.empty?
+ cd "#{root}/vendor/plugins"
+ args.each do |name|
+ if File.directory?(name)
+ puts "Updating plugin: #{name}"
+ system("svn #{$verbose ? '' : '-q'} up #{name}")
+ else
+ puts "Plugin doesn't exist: #{name}"
+ end
+ end
+ end
+ end
+
+end
+
+Commands::Plugin.parse! \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
index 65d6afd9ab..71e0382723 100644
--- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb
+++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
@@ -49,7 +49,7 @@ class AppGenerator < Rails::Generator::Base
m.file "environments/test.rb", "config/environments/test.rb"
# Scripts
- %w( breakpointer console destroy generate performance/benchmarker performance/profiler process/reaper process/spawner process/spinner runner server lighttpd ).each do |file|
+ %w( breakpointer console destroy generate performance/benchmarker performance/profiler process/reaper process/spawner process/spinner runner server lighttpd plugin ).each do |file|
m.file "bin/#{file}", "script/#{file}", script_options
end